你的位置:首页 > Java教程

[Java教程]Java WEB 安全认证


用户权限问题是绝大部分应用都要考虑的问题,在这些应用中想必都会定义role,user等来控制资源的访问。今天这里要说的并不是如何去设计权限的管理系统,而是来说明一下Web应用的安全域等。

         在与Java web相关的JSR中,对于Java Web的安全访问有也相关规定,具体内容就不再这里阐述。与些相关的配置有:security-constraint,security-role,login-config。 

         Web安全访问的机制其实就是一套权限处理系统。它包括了:role、user、resources、group等概念。接下来就心Tomcat的manager应用为例来了解一下web安全相关内容。

 

 

1、在Web.中使用security-role定义角色

在Manager应用在web.

<security-role>  <description>   The role that is required to access the HTML Manager pages  </description>  <role-name>manager-gui</role-name> </security-role> <security-role>  <description>   The role that is required to access the text Manager pages  </description>  <role-name>manager-script</role-name> </security-role> <security-role>  <description>   The role that is required to access the HTML JMX Proxy  </description>  <role-name>manager-jmx</role-name> </security-role> <security-role>  <description>   The role that is required to access to the Manager Status pages   </description>  <role-name>manager-status</role-name> </security-role> <security-role>  <description>   Deprecated role that can access all Manager functionality  </description>  <role-name>manager</role-name> </security-role>

 从上面的例子中,就直接可以看出角色定义是很简单的,只需要设置role-name就可以了。

2、在web.中使用security-constraint配置资源访问

Manager应用中定义了一些安全访问的限制如下:

<security-constraint>  <web-resource-collection>   <web-resource-name>Manager commands</web-resource-name>   <url-pattern>/list</url-pattern>   <url-pattern>/expire</url-pattern>   <url-pattern>/sessions</url-pattern>   <url-pattern>/start</url-pattern>   <url-pattern>/stop</url-pattern>   <url-pattern>/install</url-pattern>   <url-pattern>/remove</url-pattern>   <url-pattern>/deploy</url-pattern>   <url-pattern>/undeploy</url-pattern>   <url-pattern>/reload</url-pattern>   <url-pattern>/save</url-pattern>   <url-pattern>/serverinfo</url-pattern>   <url-pattern>/roles</url-pattern>   <url-pattern>/resources</url-pattern>   <url-pattern>/findleaks</url-pattern>  </web-resource-collection>  <auth-constraint>    <!-- NOTE: 1. These roles are not present in the default users file         2. The manager role is deprecated, it will be removed in           Tomcat 7.         3. Use the manager-script role to take advantage of the new           CSRF protection. Using the manager role or assigning both           the manager-script and manager-gui roles to the same user           will bypass the CSRF protection. -->    <role-name>manager-script</role-name>    <role-name>manager</role-name>  </auth-constraint> </security-constraint> <security-constraint>  <web-resource-collection>   <web-resource-name>HTML Manager commands</web-resource-name>   <url-pattern>/html/*</url-pattern>  </web-resource-collection>  <auth-constraint>    <!-- NOTE: 1. These roles are not present in the default users file         2. The manager role is deprecated, it will be removed in           Tomcat 7.         3. Use just the manager-gui role to take advantage of the new           CSRF protection. Assigning the manager role or manager-gui           role along with either the manager-script or manager-jmx           roles to the same user will bypass the CSRF protection. -->    <role-name>manager-gui</role-name>    <role-name>manager</role-name>  </auth-constraint> </security-constraint> <security-constraint>  <web-resource-collection>   <web-resource-name>JMX proxy</web-resource-name>   <url-pattern>/jmxproxy/*</url-pattern>  </web-resource-collection>  <auth-constraint>    <!-- NOTE: 1. These roles are not present in the default users file         2. The manager role is deprecated, it will be removed in           Tomcat 7.         3. Use the manager-jmx role to take advantage of the new           CSRF protection. Using the manager role or assigning both           the manager-jmx and manager-gui roles to the same user           will bypass the CSRF protection. -->    <role-name>manager-jmx</role-name>    <role-name>manager</role-name>  </auth-constraint> </security-constraint> <security-constraint>  <web-resource-collection>   <web-resource-name>Status</web-resource-name>   <url-pattern>/status/*</url-pattern>  </web-resource-collection>  <auth-constraint>    <!-- NOTE: 1. These roles are not present in the default users file         2. The manager role is deprecated, it will be removed in           Tomcat 7. -->    <role-name>manager-status</role-name>    <role-name>manager-gui</role-name>    <role-name>manager-script</role-name>    <role-name>manager-jmx</role-name>    <role-name>manager</role-name>  </auth-constraint> </security-constraint>

 从上面的例子中很容易可以理解:security-constraint是用于对指定的角色进行url资源访问上的限制。

 

3、使用Realm来配置user

到目前为止,resources、role已经配置完毕,那么怎么去指定访问资源的用户呢?以及这些用户是拥有哪些role呢?

Realm安全域,简单的理解就是一个数据存储介质(例如

         第一种服务器中间件都会支持多种Realm的配置方式。这里只说一下在使用Tomcat时,可以基于哪些数据存储介质来配置:

 

相信看了这个类的继承关系图,就可以知道Tomcat中支持哪些配置Realm的方式了。

 

MemoryRealm 是使用tomcat/conf/tomcat-users.

JNDIRealm 是基于JNDI资源的方式配置的,

JDBCRealm 是将用户存储在数据库里。

 

3.1  JDBCRealm

在一个数据库里创建下面两张表:

create table users ( user_name     varchar(15) not null primary key, user_pass     varchar(15) not null);create table user_roles ( user_name     varchar(15) not null, role_name     varchar(15) not null, primary key (user_name, role_name));

然后在tomcat/conf/server.插入数据.

<Realm className="org.apache.catalina.realm.JDBCRealm"   driverName="org.gjt.mm.mysql.Driver"  connectionURL="jdbc:mysql://localhost/authority?user=dbuser;password=dbpass"    userTable="users" userNameCol="user_name" userCredCol="user_pass"  userRoleTable="user_roles" roleNameCol="role_name"/>

 


3.2 DataSourceRealm
 

在tomcat中定义数据源的方式就是使用JNDI Resources。

1、创建相关表

create table users ( user_name     varchar(15) not null primary key, user_pass     varchar(15) not null);create table user_roles ( user_name     varchar(15) not null, role_name     varchar(15) not null, primary key (user_name, role_name));

2、server.

<Resource name="jdbc/authority" auth="Container"      type="javax.sql.DataSource"username="dbuser" password="dbpass" url= "jdbc:mysql://localhost/authority?useEncoding=UTF-8"/>

 

如果不会配置datasource可以参考Tomcat:定义JNDI资源,访问数据库

 

3、在server.

<Realm className="org.apache.catalina.realm.DataSourceRealm"  dataSourceName="jdbc/authority"  userTable="users" userNameCol="user_name" userCredCol="user_pass"  userRoleTable="user_roles" roleNameCol="role_name"/>

 


3.3 JNDIRealm
 

虽然tomcat中使用resource定义的资源都是通过jndi方式来访问的,但是这里说的JNDIRealm是特指使用了LDAP 目录服务的数据源。

 

3.4 UserDatabaseRealm

UserDatabaseRealm是Realm的一个子类,它是通过JNDI资源的方式来获取数据的。所以需要采用Resource的定义方式来定义。例如Tomcat中默认的配置

  <Resource auth="Container" description="User database that can be updated and saved" factory="org.apache.catalina.users.MemoryUserDatabaseFactory" name="UserDatabase" pathname="conf/tomcat-users. type="org.apache.catalina.UserDatabase"/>

它定义了名为UserDatabase的资源,然后在Engine范围内使用这个Realm:

<Engine defaultHost="localhost" name="Catalina"><Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase" />

只不过数据来源于tomcat-users.

 

3.5 MemoryRealm

如果要使用MemoryRealm,需要在server.

<tomcat-users><!-- NOTE: By default, no user is included in the "manager-gui" role required to operate the "/manager/html" web application. If you wish to use this app, you must define such a user - the username and password are arbitrary.--><!-- NOTE: The sample user and role entries below are wrapped in a comment and thus are ignored when reading this file. Do not forget to remove <!.. ..> that surrounds them.--> <role rolename="tomcat"/> <role rolename="role1"/> <user username="both" password="tomcat" roles="tomcat,role1"/> <user username="role1" password="tomcat" roles="role1"/>  <user username="tomcat" password="tomcat" roles="manager-gui,manager" />  <user username="tomcat2" password="tomcat" roles="manager-gui,manager" />  <user username="tomcat3" password="tomcat" roles="manager-gui,manager" />  <user username="tomcat4" password="tomcat" roles="manager-gui,manager" /></tomcat-users>

 


3.6 JAASRealm
 

如果你了解JAAS的话,JAASRealm的方式你就很容易学会的。JAAS 是Java Authentication & Authorization Service (JAAS) framework ,是J2SE API的一部分。这个就不再详细说明。

 

3.7 CombinedRealm

顾名思义,就是结合了多种Realm方式。例如:

<Realm className="org.apache.catalina.realm.CombinedRealm" >  <Realm className="org.apache.catalina.realm.UserDatabaseRealm"       resourceName="UserDatabase"/>  <Realm className="org.apache.catalina.realm.DataSourceRealm"       dataSourceName="jdbc/authority"       userTable="users" userNameCol="user_name" userCredCol="user_pass"       userRoleTable="user_roles" roleNameCol="role_name"/></Realm>

 

3.8 LockOutRealm 

LockOutRealm 是 CombinedRealm的子类,它额外的提供了锁出机制。这里就不再详细说明了。

 

4 认证方式

         到目前为此,user( & group) 、role、resouces都配置完毕了,那么要让服务器采用哪种认证方式呢?

         目前的服务器中间件都支持下列几种方式: BASIC、DIGEST、CLIENT_CERT、FORM。这几种方式是标准的。

对于tomcat来说,它同样也支持这几种。

 

要配置采用哪种认证方式很简单,只需要在web.

 

4.1 BASIC

Basic方式是最简单的方式,用户若访问受限的资源时,浏览器就会弹出一个对话框,让输入用户名和密码。浏览器在发送请求时,会使用Base64进行编码。

<login-config>    <auth-method>BASIC</auth-method>  </login-config>

查看认证过程的部分源码: 

if (authorization != null) {      authorization.toBytes();      ByteChunk authorizationBC = authorization.getByteChunk();      if (authorizationBC.startsWithIgnoreCase("basic ", 0)) {        authorizationBC.setOffset(authorizationBC.getOffset() + 6);        // FIXME: Add trimming        // authorizationBC.trim();                CharChunk authorizationCC = authorization.getCharChunk();        Base64.decode(authorizationBC, authorizationCC);                // Get username and password        int colon = authorizationCC.indexOf(':');        if (colon < 0) {          username = authorizationCC.toString();        } else {          char[] buf = authorizationCC.getBuffer();          username = new String(buf, 0, colon);          password = new String(buf, colon + 1,               authorizationCC.getEnd() - colon - 1);        }                authorizationBC.setOffset(authorizationBC.getOffset() - 6);      }      principal = context.getRealm().authenticate(username, password);      if (principal != null) {        register(request, response, principal, Constants.BASIC_METHOD,             username, password);        return (true);      }    }    

 认证前,使用Base64解码取得用户名和密码,然后进行认证。

 

4.2 FORM

使用FORM方式时,就需要在自己定制登录页面:

<login-config>    <auth-method>FORM</auth-method>    <form-login-config>      <form-login-page>/login.html</form-login-page>      <form-error-page>/error.jsp</form-error-page>    </form-login-config>  </login-config>

login页面如下:

<form name="loginform" method="post" action="j_security_check"><INPUT name="j_username" type="text"><INPUT name="j_password" TYPE="password"><input type="submit" value="登 录" ></form>

其中表单的action值,以及代表用户名、密码的input的name值是固定的。

 

下面是部分源码:

String username = request.getParameter(Constants.FORM_USERNAME);    String password = request.getParameter(Constants.FORM_PASSWORD);    if (log.isDebugEnabled())      log.debug("Authenticating username '" + username + "'");    principal = realm.authenticate(username, password);    if (principal == null) {      forwardToErrorPage(request, response, config);      return (false);    }

 认证失败就会转到error-page了。

 

4.3 DIGEST

<login-config>    <auth-method>DIGEST</auth-method>  </login-config>

Digest,采用的是MD5摘要算法。可以参考源码:

      // Second MD5 digest used to calculate the digest :      // MD5(Method + ":" + uri)      String a2 = method + ":" + uri;      byte[] buffer;      synchronized (md5Helper) {        buffer = md5Helper.digest(a2.getBytes());      }      String md5a2 = md5Encoder.encode(buffer);      return realm.authenticate(userName, response, nonce, nc, cnonce,          qop, realmName, md5a2);

 

 

4.4 CLIENT_CERT

<login-config>    <auth-method>CLIENT-CERT</auth-method>  </login-config>

采用的是X509证书认证。 

 

 

5、安全认证流程

      当用户访问的是一个受限的资源(也就是security-contistans中配置的url)时,应付触发安全认证。安全认证的过程:Server通过不同形式(基于认证方式的不同)取得用户的username和password,与Realm中存储的值匹配,匹配成功后就得知访问用户所拥有的roles,再根据roles判断是否能够访问的资源。如果这些流程都通过,会将用户信息存储在session中,这样就不需要每次访问受限资源都输入用户名和密码,之后就能够访问到资源了。中间过程有一步出错,就会访问失败。