锘??xml version="1.0" encoding="utf-8" standalone="yes"?>
How do I implement security, one of the most important aspects of today's software applications, into my Java environment when most security implementations are inflexible, proprietary systems?
The Java Authentication and Authorization Service (JAAS) is a flexible, standardized API that supports runtime pluggability of security modules.
In practice, JAAS represents the new Java security standard, as it has formally been added to the JDK 1.4 code base. From an architectural standpoint, JAAS implements a Java version of the Pluggable Authentication Module (PAM) framework. First released in May 2000 by The PAM Forum, the framework is a modularized architecture designed to support the seamless exchange of one security protocol component for another. The framework allows multiple authentication technologies and/or authentication approaches to be added without changing or interfering with any of the existing login services. PAM can be used to integrate login services with various authentication technologies, such as RSA, DCE, Kerberos, S/Key, and even to support smart card-based authentication systems.
Authenticating with JAAS
JAAS authentication is deployed in a pluggable manner, using code modules that implement certain interfaces. This enables Java applications to remain decoupled from the underlying authentication technologies. Additional authentication protocols and updated authentication technologies can be plugged in at runtime without modifying the application or recompiling the source code.
The JAAS Authentication API is quite extensive. The key interfaces and classes that you need to familiarize yourself with are as follows:
After a user has been authenticated by JAAS, the authorization API associates the Subject (created to represent the authenticated entity) with an appropriate access control context. Whenever the Subject attempts a restricted operation (database access, local file access, etc.), the Java runtime consults the policy file to determine which Principal(s) may perform the operation. If the Subject in question contains the designated Principal, the Java runtime allows the operation. Otherwise, it throws an exception.
You don't need to import additional packages to access the JAAS authorization features, because JAAS authorization is built on top of JAAS authentication. In addition to the classes and interfaces used in the authentication piece, one additional interface is of interest for the simple example in this Solution:
![]() |
Authentication Files
Authorization Files
To test the application, run the provided script and indicate whether you want to test just authentication ('run auth') or authentication and authorization ('run authz'). When prompted for a username and password, provide any of the following pairs:
You will receive verbose output if the debug option in the config file debug property is set to 'true'. The output will be limited if it is set to 'false'.
With the increasing use of distributed systems, users often need to access multiple resources to finish a single business transaction. Traditionally, users have had to sign on to all these systems, each of which may involve different usernames and authentication requirements. With the introduction of the single sign-on technique, users can login once and be authenticated for all systems involved in a given business transaction.
Although the single sign-on concept is appealing, implementing it is not at all easy because enterprise systems often have varying security requirements and a wide range of underlying technologies on which they are deployed. In Java environments, Java Authentication and Authorization Service (JAAS) has made implementation easier. JAAS, a significant enhancement to the Java security architecture, is an ideal tool for access control in a multi-user environment where users must be granted varying privileges. Unlike the standard JDK security model, which is code-source-based permission checking, JAAS controls access to resources with both code-source-based and user/role-based permission checking. Most importantly, the "pluggable" and stackable login module architecture that JAAS defines makes it instrumental in supporting legacy security implementations on different technologies and serves as a useful mechanism for implementing single sign-on.
Single sign-on can be implemented for applications that are deployed either locally or over a network. In the case of a network, after the user logs into the primary domain, an encrypted secure token is created and sent over the wire to other applications. In local networks, user credential data is exchanged directly between applications. Both deployment options share two key challenges: passing user credential information between involved domains and translating this information.
An enterprise application can be comprised of several Web applications, each of which may depend on different technologies and data stores to retrieve the user information it needs to authenticate the user and determine his or her privilege. If a business transaction crosses the Web application boundary, the user needs to log into each Web application and present similar credentials to each of the application authentication services.
This article demonstrates a single sign-on implementation for multiple Web applications based on JAAS. Specifically, it introduces an approach to achieving single sign-on between Web applications deployed on the same application server.
A user has to be authenticated before he/she can access any sensitive resources. After the user is successfully authenticated, his/her principal is populated in the associated Subject class and the permissions granted to the principal are then checked by the authorization component.
Pluggable Authentication Module (PAM) Framework
The JAAS authentication framework is based on Pluggable Authentication Module (PAM). JAAS authentication is performed in a pluggable fashion that allows an application to add more authentication modules. Java applications can remain independent from underlying authentication technologies, and thus both legacy and new authentication technologies can be seamlessly configured without requiring modifications to the application itself.
The JAAS authentication framework allows applications to define any number of login modules in the configuration file. The JAAS framework invokes these login modules in the order they were specified. Applications can also specify a flag to each login module to indicate the relative importance of that module. The overall authentication depends on the combined results of these individual authentication modules.
PAM allows different Web applications to authenticate users against its own data store, be it an LDAP server, relational database, NT domain, or even a file. PAM is the feature that enables developers to implement single sign-on among Web applications deployed on the same application server.
LoginModule
The LoginModule interface gives developers the ability to implement different kinds of authentication technologies that can be plugged under an application. For example, one type of LoginModule may perform a username/password-based form of authentication. Other LoginModules may interface to hardware devices such as smart cards or biometric devices.
The ability to pass optional share information between login modules is the key feature I employ to achieve single sign-on among Web applications. User credential information can be shared using this option and since it is not sent across the network, there is no need for extra effort to maintain the integrity and security of the credential information.
Login Configuration for Single Sign-On
When multiple Web applications are deployed on a single application server instance, usually each Web application authenticates users based on its own data store. One Web application may use a relational database to store user security information and another may use an LDAP server to authenticate users. The JAAS PAM architecture allows an enterprise application to define a stack of login modules, each of which is independent and communicates to its own data source. To achieve single sign-on, each Web application defines its own login module and all modules are stacked in a certain order in the login configuration file.
JAAS defines a configuration interface, which a user can implement to store configuration data in a file or a database. In this discussion, I assume a file-based login configuration and I use a Logistics enterprise application as an example. The Logistics enterprise application contains two Web applications: carrier Web application and shipper Web application. Carrier users log into the carrier Web application to report location and movement events for shipment and Shipper users log into the shipper Web application to query the delivery events of shipment.
If a 3PL (third-part logistics) uses this Logistics enterprise application, employees of the 3PL will need to log into both Web applications to maintain and query shipment status on behalf of both the carrier and shipper. To achieve single sign-on in this situation based on JAAS, the login configuration file needs to be defined as follows:
LogisticsEnterpriseApplication
{
com.cysive.framework.security.ldap.LDAPLoginModule
required name=CarrierSecurityDomain;
com.cysive.framework.security.ldap.LDAPLoginModule
required name=ShipperSecurityDomain useSharedState=true;
};
Notice two LDAP login modules are defined: one for the carrier Web application and one for the shipper Web application. These two login modules may communicate to the same LDAP server or different ones, depending on the configuration (using name attribute to link to the particular configuration block) for the LDAP server. For single sign-on, the flags for both login modules are specified as required (other flags include optional, sufficient, and requisite), which means the user must be successfully authenticated by both LDAP servers.
Additionally, the attribute useSharedState is specified. If it is true, this LoginModule retrieves the username and password from the module's shared state, using "javax.security.auth.login.name" and "javax.security.auth.login.password" as the respective keys. The retrieved values are used for authentication. The username and password are set into shared state by LoginContext before invoking the login method of the login module instance. The useSharedState attribute allows all login modules to share the user's credential information, which is captured only once.
An XML-based Configuration for Login Module
When the login module communicates with the data store, be it a database or an LDAP server, it requires certain configurations for common or special attributes. Using the LDAP login module for example, it will at least know the hostname of the LDAP server. Depending on your implementation, you may need different configuration parameters. Listing 1 shows a sample configuration block for the two LDAP login modules defined in the previous sections.
This configuration needs to be loaded when the application server starts up so it can be shared by Web applications loaded later. I will not discuss each entry in detail but instead focus on the parts that are relevant to single sign-on.
While each block is being loaded, the initializer class is invoked. The initializer reads all the properties and registers them in the domain manager class with the name specified by the name attribute. The value of this name attribute is the same as the one specified in the login configuration file. This is how the login module entry is associated with its configuration block.
If the configuration is simple, you can even configure it when you define the login module in the configuration file, such as:
com.cysive.framework.security.ldap.LDAPLoginModule
required DriverName="oracle.jdbc.driver.OracleDriver"
InitialCapacity="0" MaxCapacity="10" Properties="user=cymbio;password=cymbio
URL="jdbc:oracle:thin:@jtao1:1521:jtao1";
However, if your initialization for a login module requires substantial setup, a separate initialization block is the preferred approach.
In the two sample LDAP initialization blocks, the carrier security domain configures the login module to talk to the carrier.cysive.com LDAP server while the shipper login module communicates with shipper.cysive.com. You can easily substitute one of them with a relational database login module if user security information is stored in a database.
In the login method of LoginContext, each login module defined in the configuration file is instantiated and then is passed four parameters using the initialize method of the login module (See Listing 2, exception checking is omitted for smaller code).
The following are the four parameters the initialize method passes:
The shared state map parameter is key to implementing single sign-on. After the first login module captures the user credential information, such as user name and password, it puts the information into the shared state map and passes it to other login modules on the list. The other login module has an optional parameter specified (useSharedState=true), so it simply gets the shared security information back and authenticates against its own security data store. Listing 3 shows a code excerpt for the LoginModule that implements this (exception checking has been omitted to reduce the length of the code segment).
ReMapping of Security Information
Using a shared state map to pass security information between login modules to achieve single sign-on requires the user to have the same credential information across all domains. I could enforce the same username and password for all domains that involve single sign-on, but that may limit legacy security systems that have varying user name and password requirements. For example, different authentication mechanisms may have their own distinctive password requirements with regard to length, characters allowed, and so forth. These requirements make using the same username and password for multiple login modules problematic.
In their paper, "Making Login Services Independent of Authentication Technologies", Vipin Samar and Charlie Lai proposed a mapping mechanism that solves this problem. This mapping enables the user's primary password to be used for encrypting the user's secondary passwords. Once the primary password is verified, the login module obtains the password by decrypting the mechanism-specific encrypted password with the primary password. It then authenticates it to its data store. How the password is encrypted depends completely on the module implementation. To use password mapping, Samar and Lai also suggest two more optional attributes be passed to the login module:
Shared Map Is Key
Single sign-on for multiple Web applications is useful when business transactions cross session context boundaries. The pluggable and stackable login module infrastructure provided by JAAS allows enterprise applications to authenticate users with different login mechanisms and technologies. Exchanging security information between login modules using the shared map is the key to implementing single sign-on.
As I wrote in a DevX 10-Minute Solution, "JAAS Security in Action": JAAS 鈥渋s a flexible, standardized API that supports runtime pluggability of security modules.鈥?If you are unfamiliar with JAAS, I recommend reading that article and reviewing the downloadable code before continuing, as this article assumes an understanding of JAAS. It takes the next logical step from a security architecture standpoint: integrating your J2EE security model to provide SSO across multiple subsystems by leveraging your existing LDAP directory server, database server, or any other enterprise security system.
Before going any further, let's clarify how this article uses the term "domain": It refers to security domains (LDAP, database, etc.) and not Web domains. If you are interested in using JAAS to share authentication information between multiple Web applications, read the article "Implement Single Sign-on with JAAS" written by James Tao in October of 2002. Additionally, if you are interested in Web applications that exist across firewalls and participate in some sort of Web service exchange, read the joint Web Single Sign-On Identify specifications that Microsoft and Sun recently published.
Regardless of whether the deployment scenario is local, across a network, or a combination of the two, the security challenges are the same: sharing credentials between domains, correctly interpreting the credentials once received, and managing different sets of privileges across these domains (e.g., a user could be a manager within one system, a power user in another system, and a normal user in a third).
Finally, the heterogeneous nature of most enterprise systems creates some unique challenges for SSO security architectures. Each application within the enterprise could be comprised of different technologies, operate on different platforms, access disparate data sources, and except slightly different authentication credentials for the same principal (user). In spite of these overwhelming obstacles, JAAS combined with LDAP provides a solid framework for designing and implementing a robust SSO enterprise security framework.
The ArchitectureThe backbone of a J2EE SSO architecture is the standard J2EE security model, which is well documented in other places (see Related Resources in the left-hand column). In a nutshell, J2EE security consists of principals (users) who are associated with roles (groups) that are given privileges (authorization). These roles with assigned privileges are further organized under the concept of a realm (domain). Each realm maps users and groups to privileges within its own scope. The key to providing SSO is seamlessly connecting these different realms (and corresponding enterprise systems) without requiring the user to enter authentication information each time he or she wishes to access another system.Consider the following example: A user logs in to an application via HTTP, authenticating herself against the server's security realm (MemoryRealm, JDBCRealm, JAASRealm, etc). The user then uses the Web application's search feature, querying the database and returning a resultlist. The database could then require that the middleware platform authenticate against the DB before performing the transaction. Finally, the user wants to update information stored in her directory server (LDAP). This is a privileged action, requiring the user to first authenticate against the LDAP realm before modifying any directory data. All three of these realms likely require slightly different authentication schemes (different user IDs, passwords, additional security tokens, etc.), but the same principal (user) is accessing them each time. Java can provide an elegant SSO solution for the above scenario (and any number of similar scenarios) using JAAS's pluggable login module architecture. JAAS login modules facilitate the smooth integration of J2EE's security framework with various systems and their respective heterogeneous authentication mechanisms (OS, LDAP, database, etc.). These modules can be configured to share authentication data and designed to correctly identify users and roles by mapping principals and roles鈥攅ven across domains with differing security schemas.
The ComponentsThe application components required for a JAAS SSO solution include the following:
Assembling these components and connecting all of the pieces correctly can be a bit daunting the first time. Be sure to thoroughly test your JAAS authentication components individually with each system prior to attempting to link them and share authentication information. The process of packaging, deploying, and testing your solution should go something like this:
The above list simply gives you a brief overview of the process. For more details on how to actually accomplish these steps, please consult the links in the Related Resources space.
|
Sharing Authentication Across DomainsThe JAAS configuration file mentioned earlier defines configuration IDs, which contain one or moreLoginModule definitions and corresponding attributes. The key to sharing authentication information across security domains is to have a single JAAS configuration ID that lists multiple login modules, with the second, third, and following modules specifying the useSharedState attribute as true (allowing security credentials to be shared).
The following is a sample configuration involving two systems, LDAP and an Oracle database, included within the same SSO architecture:
Two configurations are defined (BasicAuth and DBAccessAuth) to provide the flexibility of authenticating a user against one or both systems. To implement this, the first system (LDAP) is treated as the primary system. The corresponding login module should be setup as the security realm for the J2EE container (using the BasicAuth configuration). Once a user has authenticated against the primary system (see Figure 1), she can have her security credentials propagated to the other system(s) in the chain (using the DBAccessAuth) that require authenticated access (see Figure 2).
This simple example contains only two subsystems, but a more complex system could include as many JAAS login modules as systems that need to share security credentials. The key difference between how authentication occurs with the two subsystems is that the primary system has JAAS authentication handled automatically (the container manages the process since the login module is configured as a security realm). The secondary subsystem (as well as any subsequent systems) must be called programmatically via the
The above example assumed a fairly ideal situation in which the user's security credentials were the same for both subsystems. In real life, this is rarely the case. You often find yourself needing to integrate legacy systems with new applications, each requiring different credentials. You can accomplish this by using the original authentication information to unlock a secured datastore that houses secondary authentication information for the specified principal (one or more sets of credentials). Once you've obtained this other authentication information, you then can use it to authenticate transparently against any secondary systems. (For more details on this approach, read Vipin Samar and Charlie Lai's article (PDF): "Making Login Services Independent of Authentication Technologies".) Voila! Single sign-on across multiple security domains by mapping primary credentials gathered from the user to secondary credentials stored on the server and used to authenticate transparently against other enterprise systems.
Single SSO in Your Enterprise FrameworkSSO is a popular feature of modern enterprise systems. Unfortunately, implementing SSO can be difficult and error-prone. This brief article provides a high-level overview and guide for architects, developers, and/or managers who are interested in implementing a SSO security architecture within their current enterprise frameworks.
Kyle Gabhart is a Senior Principal Information Systems Architect with L-3 Communications, Link Simulation and Training Division. Author of dozens of articles and books, Kyle has served as a consultant, trainer, and mentor for Fortune 500 companies, including American Express, J.P. Morgan-Chase, Goldman Sachs, and Verizon. Find Kyle on the Web at http://www.gabhart.com and reach him by email at rkgabhart@link.com. |
<filter> <filter-name>NtlmHttpFilter</filter-name> <filter-class>jcifs.http.NtlmHttpFilter</filter-class> <init-param> <param-name>jcifs.smb.client.domain</param-name> <param-value>NYC-USERS</param-value> </init-param> <init-param> <param-name>jcifs.netbios.wins</param-name> <param-value>10.169.10.77,10.169.10.66</param-value> </init-param> </filter> <filter-mapping> <filter-name>NtlmHttpFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>This filter section illustrates the setup for authenticating clients against the domain NYC-USERS. This is suitable for large numbers of concurrent users as jCIFS will cycle through domain controllers and use an alternate WINS server if necessary. The above will authenticate users accessing all content against the domain NYC-USERS. The WINS server 10.169.10.77 will be queried to resolve NYC-USERS to an IP address of a domain controller. If that WINS server is not responding, 10.169.10.66 will be queried.
<filter> <filter-name>NtlmHttpFilter</filter-name> <filter-class>jcifs.http.NtlmHttpFilter</filter-class> <init-param> <param-name>jcifs.http.domainController</param-name> <param-value>192.168.2.15</param-value> </init-param> <init-param> <param-name>jcifs.smb.client.logonShare</param-name> <param-value>JCIFSACL</param-value> </init-param> </filter> <filter-mapping> <filter-name>NtlmHttpFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>This filter section illustrates the setup for authenticating against a JCIFSACL share for testing or a site with a small number of concurrent users (e.g. 1000) Either a jcifs.smb.client.domain or jcifs.smb.client.domainController property is required. This will be suitable to authenticate clients that are members of the specified domain as well as other domains with which it has trusts relationships. Running the NtlmHttpAuthExample.java example should be a suitable test of the Filter.
The significance of the POST test is that after negotiating NTLM HTTP Authentication once, IE will not POST any form data until it has negotiated the password hashes again. If the NTLM HTTP Authentication Filter is not enabled something like the following will be displayed:NTLM HTTP Authentication Example
NYC-USERS\MIALLEN successfully logged inPlease submit some form data using POST
field1 = hello
null successfully logged inNotice the user was permitted access. Unlike this example, developers might add an additional check to make sure getRemoteUser does not return null.
jcifs.smb.client.domain | The NT domain against which clients should be authenticated. Generally it is necessary to also set the jcifs.netbios.wins parameter or a domain controller may not be found. This parameter will be ignored for NTLM HTTP authentication purposes if a jcifs.http.domainController property is specified (although they can be used together for "preauthenctication" as described in the SMB Signatures and Windows 2003 section below). |
jcifs.http.domainController | The IP address of any SMB server that should be used to authenticate HTTP clients with the NtlmHttpFilter class. If this is not specified the jcifs.smb.client.domain 0x1C NetBIOS group name will be queried. If these queries fail an UnknownHostException will be thrown. It is not necessary for this to specify a real domain controller. The IP address of a workstation will do for simple purposes. |
jcifs.http.basicRelm | The realm for basic authentication. This property defaults to 'jCIFS'. |
jcifs.http.enableBasic | Setting this property to true enables basic authentication over HTTPS only. |
jcifs.http.insecureBasic | Setting this property to true enables basic authentication over plain HTTP. This configuration passes user credentials in plain text over the network. It should not be used in environment where security is required. |
jcifs.http.loadBalance | If a jcifs.smb.client.domain property is specified (and domainController is not specified) the NtlmHttpFilter will query for domain controllers by name. If this property is true the Filter will rotate through the list of domain controllers when authenticating users. The default value is true. The jcifs.netbios.lookupRespLimit property can also be used to limit the number of domain controllers used. |
jcifs.netbios.lookupRespLimit | The 0x1C NetBIOS name query returns a list of domain controllers. It is believed that the servers at the top of this list should be favored. This property limits the range of servers returned by name queries. The default value is 5 meaning the top 5 domain controllers will be used. |
jcifs.netbios.wins | The IP address of the WINS server. This is required when accessing hosts on different subnets (like a domain controller by name) and it is highly recommended if a wins server is available. |
jcifs.smb.client.laddr | The ip address of the local interface the client should bind to if it is different from the default. For example if jCIFS is used to authenticate clients on one interface and the domain controller for those clients is accessible only on another interface of a webserver with two NICs it may be necessary to specify which interface jCIFS should use. |
jcifs.netbios.laddr | The ip address of the local interface the client should bind to for name queries if it is different from the default. Likely set to the same as the above property. |
jcifs.smb.client.attrExpirationPeriod | Attributes of a file are cached for attrExpirationPeriod milliseconds. The default is 5000 but the NetworkExplorer servlet will attempt to set this property to 120000. Otherwise, when listing large directories, the attributes of SmbFiles may expire within the default period resulting in a large number of additional network messages and severe performance degradation. |
jcifs.smb.client.soTimeout | To prevent the client from holding server resources unnecessarily, sockets are closed after this time period if there is no activity. This time is specified in milliseconds. The default is 15000 however when NTLM HTTP Authentication is used, the NtlmHttpFilter will attempt to set this value to 5 minutes so that frequent calls to SmbSession.logon() do not provoke redundant messages being submitted to the domain controller. If it is not desirable to cache password hashes set this value back to 15000. |
jcifs.netbios.cachePolicy | When a NetBIOS name is resolved with the NbtAddress class it is cached to reduce redundant name queries. This property controls how long, in seconds, these names are cached. The default is 30 seconds, 0 is no caching, and -1 is forever. When NTLM HTTP Authentication is used, NtlmHttpFilter will attempt to set this value to 20 minutes so that frequent queries for a domain controller will be cached. |
Exception MalformedURLException: unknown protocol: smb at java.net.URL.(URL.java:480) at java.net.URL.(URL.java:376) at java.net.URL.(URL.java:330) at jcifs.smb.SmbFile.(SmbFile.java:355) ...
<init-parameter> <parameter-name>jcifs.netbios.hostname</parameter-name> <parameter-value>MYHOSTNAME</parameter-value> </init-parameter>
http://davenport.sourceforge.net/ntlm.html
http://www.innovation.ch/java/ntlm.html
This is an attempt at documenting the undocumented NTLM authentication scheme used by M$'s browsers, proxies, and servers (MSIE and IIS); this scheme is also sometimes referred to as the NT challenge/response (NTCR) scheme. Most of the info here is derived from three sources (see also the Resources section at the end of this document): Paul Ashton's work on the NTLM security holes, the encryption documentation from Samba, and network snooping. Since most of this info is reverse-engineered it is bound to contain errors; however, at least one client and one server have been implemented according to this data and work successfully in conjunction with M$'s browsers, proxies and servers.
Note that this scheme is not as secure as Digest and some other schemes; it is slightly better than the Basic authentication scheme, however.
Also note that this scheme is not an http authentication scheme - it's a connection authentication scheme which happens to (mis-)use http status codes and headers (and even those incorrectly).
When a client needs to authenticate itself to a proxy or server using the NTLM scheme then the following 4-way handshake takes place (only parts of the request and status line and the relevant headers are shown here; "C" is the client, "S" the server):
1: C --> S GET ... 2: C <-- S 401 Unauthorized WWW-Authenticate: NTLM 3: C --> S GET ... Authorization: NTLM <base64-encoded type-1-message> 4: C <-- S 401 Unauthorized WWW-Authenticate: NTLM <base64-encoded type-2-message> 5: C --> S GET ... Authorization: NTLM <base64-encoded type-3-message> 6: C <-- S 200 Ok
The field flags is presumed to contain flags, but their significance is unknown; the values given are just those found in the packet traces.
This message contains the host name and the NT domain name of the client.
struct { byte protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' byte type; // 0x01 byte zero[3]; short flags; // 0xb203 byte zero[2]; short dom_len; // domain string length short dom_len; // domain string length short dom_off; // domain string offset byte zero[2]; short host_len; // host string length short host_len; // host string length short host_off; // host string offset (always 0x20) byte zero[2]; byte host[*]; // host string (ASCII) byte dom[*]; // domain string (ASCII) } type-1-message
0 1 2 3 +-------+-------+-------+-------+ 0: | 'N' | 'T' | 'L' | 'M' | +-------+-------+-------+-------+ 4: | 'S' | 'S' | 'P' | 0 | +-------+-------+-------+-------+ 8: | 1 | 0 | 0 | 0 | +-------+-------+-------+-------+ 12: | 0x03 | 0xb2 | 0 | 0 | +-------+-------+-------+-------+ 16: | domain length | domain length | +-------+-------+-------+-------+ 20: | domain offset | 0 | 0 | +-------+-------+-------+-------+ 24: | host length | host length | +-------+-------+-------+-------+ 28: | host offset | 0 | 0 | +-------+-------+-------+-------+ 32: | host string | + + . . . . + +-----------------+ | | domain string | +-------------+ + . . . . +-------+-------+-------+-------+The host and domain strings are ASCII (or possibly ISO-8859-1), are uppercased, and are not nul-terminated. The host name is only the host name, not the FQDN (e.g. just "GOOFY", not "GOOFY.DISNEY.COM"). The offsets refer to the offset of the specific field within the message, and the lengths are the length of specified field. For example, in the above message
host_off = 32
and dom_off = host_off + host_len
. Note that the lengths are included twice (for some unfathomable reason).
This message contains the server's NTLM challenge.
struct { byte protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' byte type; // 0x02 byte zero[7]; short msg_len; // 0x28 byte zero[2]; short flags; // 0x8201 byte zero[2]; byte nonce[8]; // nonce byte zero[8]; } type-2-message
0 1 2 3 +-------+-------+-------+-------+ 0: | 'N' | 'T' | 'L' | 'M' | +-------+-------+-------+-------+ 4: | 'S' | 'S' | 'P' | 0 | +-------+-------+-------+-------+ 8: | 2 | 0 | 0 | 0 | +-------+-------+-------+-------+ 12: | 0 | 0 | 0 | 0 | +-------+-------+-------+-------+ 16: | message len | 0 | 0 | +-------+-------+-------+-------+ 20: | 0x01 | 0x82 | 0 | 0 | +-------+-------+-------+-------+ 24: | | + server nonce | 28: | | +-------+-------+-------+-------+ 32: | 0 | 0 | 0 | 0 | +-------+-------+-------+-------+ 36: | 0 | 0 | 0 | 0 | +-------+-------+-------+-------+The nonce is used by the client to create the LanManager and NT responses (see Password Hashes). It is an array of 8 arbitrary bytes. The message length field contains the length of the complete message, which in this case is always 40.
This message contains the username, host name, NT domain name, and the two "responses".
struct { byte protocol[8]; // 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' byte type; // 0x03 byte zero[3]; short lm_resp_len; // LanManager response length (always 0x18) short lm_resp_len; // LanManager response length (always 0x18) short lm_resp_off; // LanManager response offset byte zero[2]; short nt_resp_len; // NT response length (always 0x18) short nt_resp_len; // NT response length (always 0x18) short nt_resp_off; // NT response offset byte zero[2]; short dom_len; // domain string length short dom_len; // domain string length short dom_off; // domain string offset (always 0x40) byte zero[2]; short user_len; // username string length short user_len; // username string length short user_off; // username string offset byte zero[2]; short host_len; // host string length short host_len; // host string length short host_off; // host string offset byte zero[6]; short msg_len; // message length byte zero[2]; short flags; // 0x8201 byte zero[2]; byte dom[*]; // domain string (unicode UTF-16LE) byte user[*]; // username string (unicode UTF-16LE) byte host[*]; // host string (unicode UTF-16LE) byte lm_resp[*]; // LanManager response byte nt_resp[*]; // NT response } type-3-message
0 1 2 3 +-------+-------+-------+-------+ 0: | 'N' | 'T' | 'L' | 'M' | +-------+-------+-------+-------+ 4: | 'S' | 'S' | 'P' | 0 | +-------+-------+-------+-------+ 8: | 3 | 0 | 0 | 0 | +-------+-------+-------+-------+ 12: | LM-resp len | LM-Resp len | +-------+-------+-------+-------+ 16: | LM-resp off | 0 | 0 | +-------+-------+-------+-------+ 20: | NT-resp len | NT-Resp len | +-------+-------+-------+-------+ 24: | NT-resp off | 0 | 0 | +-------+-------+-------+-------+ 28: | domain length | domain length | +-------+-------+-------+-------+ 32: | domain offset | 0 | 0 | +-------+-------+-------+-------+ 36: | user length | user length | +-------+-------+-------+-------+ 40: | user offset | 0 | 0 | +-------+-------+-------+-------+ 44: | host length | host length | +-------+-------+-------+-------+ 48: | host offset | 0 | 0 | +-------+-------+-------+-------+ 52: | 0 | 0 | 0 | 0 | +-------+-------+-------+-------+ 56: | message len | 0 | 0 | +-------+-------+-------+-------+ 60: | 0x01 | 0x82 | 0 | 0 | +-------+-------+-------+-------+ 64: | domain string | + + . . . . + +-------------------+ | | user string | +-----------+ + . . . . + +-------------+ | | host string | +-----------------+ + . . . . + +---------------------------+ | | LanManager-response | +---+ + . . . . + +------------------+ | | NT-response | +------------+ + . . . . +-------+-------+-------+-------+
The host, domain, and username strings are in Unicode (UTF-16, little-endian) and are not nul-terminated; the host and domain names are in upper case. The lengths of the response strings are 24.
To calculate the two response strings two password hashes are used: the LanManager password hash and the NT password hash. These are described in detail at the beginning of the Samba ENCRYPTION.html document. However, a few things are not clear (such as what the magic constant for the LanManager hash is), so here is some almost-C code which calculates the two responses. Inputs are passw and nonce, the results are in lm_resp and nt_resp.
/* setup LanManager password */ char lm_pw[14]; int len = strlen(passw); if (len > 14) len = 14; for (idx=0; idx<len; idx++) lm_pw[idx] = toupper(passw[idx]); for (; idx<14; idx++) lm_pw[idx] = 0; /* create LanManager hashed password */ unsigned char magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }; unsigned char lm_hpw[21]; des_key_schedule ks; setup_des_key(lm_pw, ks); des_ecb_encrypt(magic, lm_hpw, ks); setup_des_key(lm_pw+7, ks); des_ecb_encrypt(magic, lm_hpw+8, ks); memset(lm_hpw+16, 0, 5); /* create NT hashed password */ int len = strlen(passw); char nt_pw[2*len]; for (idx=0; idx<len; idx++) { nt_pw[2*idx] = passw[idx]; nt_pw[2*idx+1] = 0; } unsigned char nt_hpw[21]; MD4_CTX context; MD4Init(&context); MD4Update(&context, nt_pw, 2*len); MD4Final(nt_hpw, &context); memset(nt_hpw+16, 0, 5); /* create responses */ unsigned char lm_resp[24], nt_resp[24]; calc_resp(lm_hpw, nonce, lm_resp); calc_resp(nt_hpw, nonce, nt_resp);
Helpers:
/* * takes a 21 byte array and treats it as 3 56-bit DES keys. The * 8 byte plaintext is encrypted with each key and the resulting 24 * bytes are stored in the results array. */ void calc_resp(unsigned char *keys, unsigned char *plaintext, unsigned char *results) { des_key_schedule ks; setup_des_key(keys, ks); des_ecb_encrypt((des_cblock*) plaintext, (des_cblock*) results, ks, DES_ENCRYPT); setup_des_key(keys+7, ks); des_ecb_encrypt((des_cblock*) plaintext, (des_cblock*) (results+8), ks, DES_ENCRYPT); setup_des_key(keys+14, ks); des_ecb_encrypt((des_cblock*) plaintext, (des_cblock*) (results+16), ks, DES_ENCRYPT); } /* * turns a 56 bit key into the 64 bit, odd parity key and sets the key. * The key schedule ks is also set. */ void setup_des_key(unsigned char key_56[], des_key_schedule ks) { des_cblock key; key[0] = key_56[0]; key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); key[7] = (key_56[6] << 1) & 0xFF; des_set_odd_parity(&key); des_set_key(&key, ks); }
As mentioned above, this scheme authenticates connections, not requests. This manifests itself in that the network connection must be kept alive during the second part of the handshake, i.e. between the receiving of the type-2 message from the server (step 4) and the sending of the type-3 message (step 5). Each time the connection is closed this second part (steps 3 through 6) must be repeated over the new connection (i.e. it's not enough to just keep sending the last type-3 message). Also, once the connection is authenticated, the Authorization header need not be sent anymore while the connection stays open, no matter what resource is accessed.
For implementations wishing to work with M$'s software this means that they must make sure they use either HTTP/1.0 keep-alive's or HTTP/1.1 persistent connections, and that they must be prepared to do the second part of the handshake each time the connection was closed and is reopened. Server implementations must also make sure that HTTP/1.0 responses contain a Content-length header (as otherwise the connection must be closed after the response), and that HTTP/1.1 responses either contain a Content-length header or use the chunked transfer encoding.
Here is an actual example of all the messages. Assume the host name is "LightCity", the NT domain name is "Ursa-Minor", the username is "Zaphod", the password is "Beeblebrox", and the server sends the nonce "SrvNonce". Then the handshake is:
C -> S GET ... S -> C 401 Unauthorized WWW-Authenticate: NTLM C -> S GET ... Authorization: NTLM TlRMTVNTUAABAAAAA7IAAAoACgApAAAACQAJACAAAABMSUdIVENJVFlVUlNBLU1JTk9S S -> C 401 Unauthorized WWW-Authenticate: NTLM TlRMTVNTUAACAAAAAAAAACgAAAABggAAU3J2Tm9uY2UAAAAAAAAAAA== C -> S GET ... Authorization: NTLM TlRMTVNTUAADAAAAGAAYAHIAAAAYABgAigAAABQAFABAAAAADAAMAFQAAAASABIAYAAAAAAAAACiAAAAAYIAAFUAUgBTAEEALQBNAEkATgBPAFIAWgBhAHAAaABvAGQATABJAEcASABUAEMASQBUAFkArYfKbe/jRoW5xDxHeoxC1gBmfWiS5+iX4OAN4xBKG/IFPwfH3agtPEia6YnhsADT S -> C 200 Ok
and the unencoded messages are:
Type-1 Message:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 0: 4e 54 4c 4d 53 53 50 00 01 00 00 00 03 b2 00 00 "NTLMSSP........." 10: 0a 00 0a 00 29 00 00 00 09 00 09 00 20 00 00 00 "....)....... ..." 20: 4c 49 47 48 54 43 49 54 59 55 52 53 41 2d 4d 49 "LIGHTCITYURSA-MI" 30: 4e 4f 52 "NOR"
Type-2 Message:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 0: 4e 54 4c 4d 53 53 50 00 02 00 00 00 00 00 00 00 "NTLMSSP........." 10: 28 00 00 00 01 82 00 00 53 72 76 4e 6f 6e 63 65 "(.......SrvNonce" 20: 00 00 00 00 00 00 00 00 "........"
Type-3 Message:
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 0: 4e 54 4c 4d 53 53 50 00 03 00 00 00 18 00 18 00 "NTLMSSP........." 10: 72 00 00 00 18 00 18 00 8a 00 00 00 14 00 14 00 "r..............." 20: 40 00 00 00 0c 00 0c 00 54 00 00 00 12 00 12 00 "@.......T......." 30: 60 00 00 00 00 00 00 00 a2 00 00 00 01 82 00 00 "`..............." 40: 55 00 52 00 53 00 41 00 2d 00 4d 00 49 00 4e 00 "U.R.S.A.-.M.I.N." 50: 4f 00 52 00 5a 00 61 00 70 00 68 00 6f 00 64 00 "O.R.Z.a.p.h.o.d." 60: 4c 00 49 00 47 00 48 00 54 00 43 00 49 00 54 00 "L.I.G.H.T.C.I.T." 70: 59 00 ad 87 ca 6d ef e3 46 85 b9 c4 3c 47 7a 8c "Y....m..F...<Gz." 80: 42 d6 00 66 7d 68 92 e7 e8 97 e0 e0 0d e3 10 4a "B..f}h.........J" 90: 1b f2 05 3f 07 c7 dd a8 2d 3c 48 9a e9 89 e1 b0 "...?....-<H....." a0: 00 d3 ".."
For reference, the intermediate hashed passwords are:
鏈枃娑夊強(qiáng)鐨勫嚑涓叧閿瓧瑙i噴錛?nbsp;
SMB: Server Message Block, 鐢ㄤ簬鍏變韓渚嬪鏂囦歡銆佹墦鍗版満銆佷覆鍙f垨鑰呮槸鍛藉悕綆¢亾絳夌敤浜庨氳鐨勬娊璞″璞★紱
CIFS: Common Internet File System, SMB鐨勫寮虹増錛學(xué)indows 2000/XP瀹炵幇浜?jiǎn)璇ュ崗璁Q?BR>JCIFS: 涓涓疄鐜頒簡(jiǎn)CIFS鐨勭函Java欏圭洰錛屾棤欏諱換浣曠殑鏈湴搴撱?/P>
JCIFS鐨勭綉鍧錛?http://jcifs.samba.org/
涓嬮潰鎴戜滑鐪嬩竴涓潪甯哥畝鍗曠殑渚嬪瓙錛屽湪榪愯榪欎釜渚嬪瓙涔嬪墠蹇呴』鍑嗗涓ゅ彴鏈哄櫒A銆丅錛屽叾涓垜浠珹鏄垜浠▼搴忚繍琛屾墍鍦ㄧ殑鏈哄櫒錛岃孊鍒欐槸琚闂殑鏈哄櫒錛屼粎鐢˙涓婄殑Guest鐢ㄦ埛錛岃屼笖A褰撳墠鐨勭敤鎴蜂笉鑳藉瓨鍦ㄤ簬B鏈哄櫒涓紝涔熷氨鏄褰撴垜浠氳繃璧勬簮綆$悊鍣ㄨ闂?\\B 鐨勬椂鍊欙紝浼?xì)瑕佹眰杈撳叆鐢ㄦ埛鍚嶄互鍙?qiáng)鍙d護(hù)錛屽涓嬪浘鎵紺猴細(xì)
榪欎釜鏃跺橝鏈哄櫒涓婄殑紼嬪簭灝變笉鑳介氳繃渚嬪 \\B\folder\1.txt 榪欐牱鐨勮礬寰勬潵璁塊棶B鏈哄櫒涓婂叡浜枃浠跺すfolder涓殑1.txt鏂囦歡錛屼篃灝辨槸璇碕ava涓嚜甯︾殑File,FileInputStream綾誨凡緇忎笉璧蜂綔鐢ㄤ簡(jiǎn)錛屼笉淇★紵 涓嶄俊浣犱斧璇曡瘯
浣嗘槸鍊熷姪浜嶫CIFS浣犲氨鍙互寰堝鏄撶殑璁塊棶鍒版枃浠?.txt鐨勫唴瀹癸紝鎴戜滑鍏堟潵鐪嬭繖涓畝鍗曠殑渚嬪瓙錛?/P>
import jcifs.smb.*;
public class Demo{
public static void main(String[] args) throws Exception{
//灝唘ser鍜宲assword鎹㈡垚鏄疊鏈哄櫒涓婄殑鐢ㄦ埛鍚嶄互鍙?qiáng)鍙d?BR> SmbFileInputStream in = new SmbFileInputStream("smb://user:password@B/folder/1.txt" );
byte[] b = new byte[8192];
int n;
while(( n = in.read( b )) > 0 ) {
System.out.write( b, 0, n );
}
}
}
緙栬瘧騫惰繍琛岃繖闈㈢殑渚嬪瓙渚垮彲浠ユ墦鍗版枃浠?1.txt 鐨勫唴瀹廣?BR>鎶婁笂闈緥瀛愪腑鐨勫瘑鐮佸~鍐欐垚涓涓敊璇殑瀵嗙爜鍐嶈繍琛岀▼搴忥紝渚夸細(xì)寰楀埌 jcifs.smb.SmbAuthException 寮傚父銆?BR>
鍐欐枃浠朵篃鏄竴涓亾鐞嗭紝鍏充簬鏂囦歡鐨勬搷浣淛CIFS鎻愪緵浜?jiǎn)杩欎箞鍑犱釜绫诲Q歋mbFile,SmbFileInputStream,SmbFileOutputStream錛屽叿浣撶殑鐢ㄦ埛璺烰ava涓搴旂殑綾誨樊涓嶅銆?/P>
JCIFS涓枃浠剁殑URL涔熷氨鏄痵mb_url錛屾牸寮忎負(fù)錛歴mb://{user}:{password}@{host}/{path} 錛屽彧瑕佸~濂借繖涓猆RL錛孞CIFS灝變細(xì)甯綘鎼炲畾韜喚楠岃瘉鐨勪簨錛岀矇綆鍗曠殑銆?/P>
鍓嶈█
鍦∕icrosoft 緗?緇?緋?緇?涓紝SMB錛圫erver Message Block錛?鏈?鍔?淇?鎭?鍧楋級(jí) 鍗?璁?鏄疻indows for Workgroup(WfWg)銆乄indows 95銆乄indows NT 鍜孡anManager 鐢?鏉?瀹?鐜?鍏?浜?灞 鍩?緗?涓?鏂?浠?鍜?鎵?鍗?鏈?鐨?鍗?璁?瀵?浜?鍒?鐢↙inux 鍜學(xué)indows NT 鏋?寤?鐨?灞 鍩?緗?鏉?璇達(dá)紝Samba 灝?鏄?涓篖inux 鎻?渚?鐨凷MB 瀹?鎴?紼?搴? 鏈?鍔?鍣?紼?搴?鐨?杞?浠?鍖咃紝 鍏?鍔?鑳?鏄?瀹?鐜癢indows 鍜孡inux 浜?鐩?鍏?浜?瀵?鏂?鐨?紓?鐩?絀?闂?鍜?鎵?鍗?鏈恒傞氱敤緗戠粶鏂囦歡緋葷粺綆縐癈IFS,瀹冧簨瀹炰笂鏄痺indows騫沖彴鏂囦歡鍏變韓鐨勬爣鍑嗗崗璁紝瀹冩槸windows explorer,緗戠粶閭誨眳鍜屾槧灝勭綉緇滈┍鍔ㄥ櫒鐨勫簳灞傚疄鐜板崗璁侸AVA鍏鋒湁澶╃劧鐨勫鉤鍙版棤鍏蟲(chóng)э紝浣跨敤JAVA鍙互璁塊棶浠諱綍綾誨瀷鐨勬湇鍔″櫒鎴栧鎴鋒満涓婄殑鍏變韓鏂囦歡緋葷粺錛屽茍涓旂紪鍐欑殑杞歡浜у搧鍙互榪愯浜庝換浣曞鉤鍙幫紝鍥犳鐢↗AVA璁塊棶鍏變韓鏂囦歡緋葷粺鍦ㄤ紒涓氬簲鐢ㄤ腑鍏鋒湁寰楀ぉ鐙帤鐨勪紭鍔褲?/P>
JAVA涓殑CIFS瀹炵幇
Jcifs鏄疌IFS鍦↗AVA涓殑涓涓疄鐜幫紝鏄痵amba緇勭粐鏈潃linux鐨勭簿紲烇紝璐熻矗緇存姢寮鍙戠殑涓涓紑婧愰」鐩傝繖涓」鐩笓娉ㄤ簬浣跨敤java璇█瀵筩ifs鍗忚鐨勮璁″拰瀹炵幇銆備粬浠皢jcifs璁捐鎴愪負(fù)涓涓畬鏁寸殑錛屼赴瀵岀殑錛屽叿鏈夊彲鎵╁睍鑳藉姏涓旂嚎紼嬪畨鍏ㄧ殑瀹㈡埛绔簱銆傝繖涓搴撳彲浠ュ簲鐢ㄤ簬鍚勭java铏氭嫙鏈鴻闂伒寰狢IFS/SMB緗戠粶浼犺緭鍗忚鐨勭綉緇滆祫婧愩傜被浼間簬java.io.File鐨勬帴鍙e艦寮忥紝鍦ㄥ綰跨▼鐨勫伐浣滄柟寮忎笅琚瘉鏄庢槸鏈夋晥鑰屽鏄撲嬌鐢ㄧ殑銆傜洰鍓峧cifs鐨勬渶鏂扮増鏈槸jcifs-0.8.0b錛屼綘鍙互鍦ㄦ湰鏂囨渶鍚庡垪鍑虹殑鍙傝冭祫鏂欎腑鑾峰緱涓嬭澆榪欎竴綾誨簱鐨勭綉鍧錛岃繖涓鐗堟湰榪樻敮鎸佸垎甯冨紡鐨勬枃浠剁郴緇燂紝鏈枃灝氫笉娑夊強(qiáng)榪欎竴鍐呭銆?/P>
JAVA/CIFS紼嬪簭璁捐
濡傛灉浣犳浘緇忎嬌鐢ㄨ繃java鐨勬枃浠舵搷浣滃姛鑳斤紝閭d箞浣犲皢寰堝鏄撴帉鎻cifs鐨勫紑鍙戞柟娉曘侸cifs閫氳繃綾諱技濡備笅鐨勮祫婧恥rl瀹氫綅涓涓祫婧愶細(xì)
smb://guest:1234@192.168.3.56/share/a.txt
榪欎釜url鐨勫紑濮嬮儴鍒唖mb:// 璇存槑浜?jiǎn)杩欐槸涓涓猻mb綾誨瀷鐨剈rl錛涙帴涓嬫潵鐨刧uest鍜?234鍒嗗埆鏄闂叡浜祫婧愮殑鐢ㄦ埛鍚嶇О鍜屽瘑鐮侊紱@鍚庨潰鏄璁塊棶鐨勮祫婧愮殑涓繪満鍚嶆垨IP鍦板潃銆傛渶鍚庢槸璧勬簮鐨勫叡浜枃浠跺す鍚嶇О鍜屽叡浜祫婧愬悕銆?
鍦↗AVA紼嬪簭涓紝浣跨敤濡備笅鏂瑰紡鑾峰緱涓涓繙紼嬪叡浜枃浠剁殑鍙ユ焺錛?
SmbFile file = new SmbFile("smb://guest:1234@192.168.3.56/share/a.txt");
榪欓噷鐨勫彞鏌勪笉浠呴檺浜庤繙紼嬬殑鍏變韓鏂囦歡錛岃繕鍙兘鏄叡浜枃浠跺す銆俰sFile()鏂規(guī)硶鍜宨sDirectory()鐢ㄦ潵鍒ゆ柇榪欎釜鍙ユ焺瀵瑰簲鐨勮祫婧愮殑鐪熷疄灞炴с傚鏋滄槸鍏變韓鏂囦歡澶癸紝閫氳繃璋冪敤瀹冪殑list()鏂規(guī)硶灝嗚幏寰楀叾涓祫婧愮殑鍒楄〃銆侺ist鏂規(guī)硶鏀寔榪囨護(hù)鍣ㄦ満鍒訛紝鏈変袱縐嶈繃婊ゅ櫒鍙緵浣跨敤錛屼竴縐嶆槸SmbFileFilter,鍙︿竴縐嶆槸SmbFilenameFilter錛岃繖涓や釜鍦╦cifs涓綔涓烘帴鍙e嚭鐜幫紝浣犲彲浠ユ牴鎹嚜宸辯殑闇瑕佹淳鐢熷嚭涓у寲鐨勮繃婊ゅ櫒錛屽疄鐜版帴鍙d腑鐨刟ccept鏂規(guī)硶錛屼互婊¤凍涓嶅悓涓氬姟鐨勯渶姹傘?
SmbFileInputStream鏄痵mb鏂囦歡鐨勮緭鍏ユ祦錛屽畠鐨勫姛鑳芥槸浠ユ祦鐨勬柟寮忔墦寮涓涓猄mbFile錛?
SmbFileInputStream in = new SmbFileInputStream(file);
SmbFileInputStream鎻愪緵read鏂規(guī)硶錛屼綘鍙互浠庤繖涓祦涓鍑?guó)櫩溄E嬫枃浠跺叏閮ㄧ殑鍐呭銆?
鑷蟲(chóng)錛屼綘宸茬粡鎺屾彙浜?jiǎn)java璁塊棶smb鏂囦歡緋葷粺鐨勫熀鏈柟娉曘?
SmbFileOutputStream鏄痵mb鏂囦歡鐨勮緭鍏ユ祦錛屽彲浠ュ悜榪滅▼鏂囦歡緋葷粺閫氳繃smb鍐欏叆鏁版嵁錛屾柟娉曚笌涓婇潰綾諱技錛屼笉鍐嶈禈榪般?
鏈枃鍚庨潰闄勫甫鐨勬簮浠g爜鏄竴涓畬鏁寸殑渚嬪瓙錛屽畠瀹屾垚鍔熻兘寰堢畝鍗曪紝灝辨槸灝嗗彟涓鍙扮數(shù)鑴戜笂鎸囧畾鍏變韓鏂囦歡澶逛笅鐨勬煇浜涙枃浠舵嫹璐濆埌鏈満鐨勬寚瀹氱洰褰曚笅銆傞氳繃榪欎釜渚嬪瓙錛岀浉淇′綘寰堝鏄撳皢瀹冪Щ妞嶅埌浣犵殑寮鍙戜駭鍝佷腑銆?/P>
絎竴姝ワ紝璁劇疆榪愯鐜銆傚皢婧愪唬鐮佽В鍘嬬緝錛屽皢lib鐩綍涓嬬殑鍚嶇О涓簀cifs-0.8.0b.jar鐨勬枃浠跺姞鍏ュ埌浣犵殑鎿嶄綔緋葷粺鐨刢lasspath涓紝浣跨ず渚嬬▼搴忚繍琛屾椂鑳藉鎵懼埌榪欎釜搴撱?/P>
絎簩姝ワ紝鍦ㄥ彟涓鍙拌綆楁満涓婂緩绔嬪悕縐頒負(fù)share鐨勫叡浜枃浠跺す,鐢ㄦ埛鍚嶄負(fù)guest,瀵嗙爜涔熶負(fù)guest銆傚亣璁捐繖鍙拌綆楁満鐨刬p鍦板潃鏄?92.168.0.2銆?/P>
絎笁姝ワ紝鍦ㄥ垰鎵嶅緩绔嬬殑鍏變韓鏂囦歡澶逛笅鏀劇疆涓や釜鏂囦歡錛屽悕縐板垎鍒負(fù)hello.txt錛宐ye.txt銆傛枃浠剁殑鍐呭鏃犳墍璋撱?/P>
絎笁姝ワ紝鍦ㄦ湰鍦拌綆楁満涓婄殑C鐩樿窡鐩綍涓嬪緩绔嬩竴涓悕縐頒負(fù)tmp鐨勭┖鐩綍錛岀疆姝ょ洰褰曞彲鍐欍?/P>
絎洓姝ワ紝緙栬瘧榪愯Sample1.java錛屽茍榪愯錛?/P>
Java Sample1 192.168.3.52 guest guest /share/ c:/tmp/
媯(gè)鏌:\tmp鏂囦歡澶?浼?xì)鍙戠幇hello.txt榪欎釜鏂囦歡琚鍒跺埌浜?jiǎn)鏈満锛岃宐ye.txt娌℃湁琚鍒訛紝鍥犱負(fù)Sample1.java涓嬌鐢ㄤ簡(jiǎn)榪囨護(hù)鍣紝浠呭厑璁稿悕縐頒負(fù)hello寮澶寸殑鏂囦歡琚嫹璐濄?/P>
鍚庤
瀵瑰叡浜枃浠剁郴緇熺殑璁塊棶鏄唴閮ㄥ姙鍏郴緇熺瓑娑夊強(qiáng)鍒版枃浠惰縼縐葷殑杞歡浜у搧寮鍙戣繃紼嬩腑涓嶅彲鍥為伩鐨勮棰橈紝jCifs鏄竴濂楅拡瀵硅繖涓璇鵑瀹屾暣鑰屽己澶х殑瑙e喅鏂規(guī)錛岄氳繃瀹冧綘鍙互瀹炵幇瀵逛換浣曞叡浜枃浠剁郴緇熺殑璁塊棶銆?/P>
涓浜涙湁鐢ㄧ殑璧勬簮鍜屽弬鑰冭祫鏂?/B>
鏈枃鐨勫叏閮ㄦ簮浠g爜錛屽彲緙栬瘧榪愯錛屼緵璇昏呯爺絀訛紝涓嬭澆婧愪唬鐮?/A>銆?/P>
濡傛浜?jiǎn)瑙f洿澶氬叧浜巎cifs鐨勪俊鎭紝璇瘋闂叾瀹樻柟緗戠珯 http://jcifs.samba.org/銆?/P>
鎮(zhèn)ㄥ彲浠ュ湪 http://users.erols.com/mballen/jcifs/ 鑾峰緱jcifs搴撶殑鏈鏂扮増鏈?/P>
濡傛灉浣犲smb榪樹(shù)笉鏄崄鍒嗙啛鎮(zhèn)夛紝榪欓噷鏈変竴綃囦笉閿欑殑鏂囩珷灝嗗悜浣犱粙緇嶄粈涔堟槸smb錛?A >http://samba.anu.edu.au/cifs/docs/what-is-smb.html
I've been pulled into a little internal project, and one of the requirements is that users should be able to authenticate with their Windows login and password. IIS may or may not be in the picture.
Since the server is a Windows 2000 machine, this turns out to be extremely simple to do thanks to Andy Armstrong's JAAS login modules.
Once you've downloaded the login modules, set your classpath accordingly and make sure that the directory holding NTSystem.dll is in your %PATH% variable. Next, in the "Sample config" folder you'll find a tagish.login
file and a java.security.sample
file. The last line in the .sample file is significant, and it needs to be in your $JAVA_HOME/jre/lib/security folder (in a file named java.security
). You should copy the tagish.login
file there as well. If your users will always be logging into the same domain (which is the case in my situation), just set the defaultDomain property in tagish.login, like this:
NTLogin { com.tagish.auth.win32.NTSystemLogin required returnNames=true returnSIDs=false defaultDomain=YOUR_DOMAIN_HERE; };
Now, all you need to do to use Windows authentication in your webapps is to make one addition to your server.xml
file (or to your specific context's definition):
<Realm className="org.apache.catalina.realm.JAASRealm" debug="99" appName="NTLogin" userClassNames="com.tagish.auth.win32.NTPrincipal" roleClassNames="com.tagish.auth.win32.NTPrincipal" />
I'll admit this config is slightly hokey. If you look at the Catalina JAASCallbackHandler (which is hardwired to JAASRealm), the way that I have the realm configured above pretty much counts on the User principal (in effect, the user name) being the first principal returned. This is evil, but it works. It would be nice if either Catalina allowed a pluggable CallbackHandler so that I could take advantage of the NTPrincipal.getType() method or if Andy's code returned subclasses of NTPrincipal like UserPrincipal or GroupPrincipal that I could specify in server.xml.
Once you've got this all configured, the various groups your users belong to equate to role names (so if I belong to an administrators group, my authenticated user will be in role "administrators"), and you can configure security in your webapps using these roles.
浜屾湀 17, 2003 04:11 涓嬪崍 MST Permalink
鐢?B minmax_bound="true">{0}鍙戣〃浜?Robert Rasmussen on 2003騫?2鏈?7鏃? 09:18 涓嬪崍 MST #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Jimmy Wong on 2003騫?2鏈?8鏃? 12:42 涓婂崍 MST #
NTSystem.dll
is in your PATH environment variable and that tagishauth.jar is in your $CATALINA_HOME/common/lib
. Currently, I believe this only works on a Windows machine. HTH, Matt
鐢?B minmax_bound="true">{0}鍙戣〃浜?Matt on 2003騫?2鏈?8鏃? 07:00 涓婂崍 MST #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Nicholas Whitehead on 2003騫?2鏈?0鏃? 11:59 涓婂崍 MST #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Roman Velichko on 2003騫?8鏈?6鏃? 12:21 涓婂崍 MDT #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Matt Raible on 2003騫?8鏈?6鏃? 08:10 涓婂崍 MDT #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Robert Rasmussen on 2003騫?8鏈?6鏃? 10:58 涓婂崍 MDT #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Tomasz Luczynski on 2004騫?3鏈?9鏃? 03:23 涓婂崍 MST #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Chris Maeda on 2004騫?7鏈?8鏃? 10:24 涓婂崍 MDT
绔欑偣錛?http://www.cmaeda.com/index.php?p=22 #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Chris Maeda on 2004騫?7鏈?8鏃? 10:38 涓婂崍 MDT
绔欑偣錛?http://cmaeda.com/index.php?p=22 #
鐢?B minmax_bound="true">{0}鍙戣〃浜?69.193.88.30 on 2004騫?1鏈?9鏃? 03:12 涓婂崍 MST
绔欑偣錛?http://www.jenniferconnor.com/ #
鐢?B minmax_bound="true">{0}鍙戣〃浜?69.193.88.30 on 2004騫?1鏈?4鏃? 01:53 涓婂崍 MST
绔欑偣錛?http://www.36busty.com/00005602.shtml #
鐢?B minmax_bound="true">{0}鍙戣〃浜?69.42.81.244 on 2005騫?1鏈?1鏃? 02:32 涓婂崍 MST
绔欑偣錛?http://www.johnhuron.com/ #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Vicky on 2005騫?4鏈?1鏃? 10:45 涓婂崍 MST #
鐢?B minmax_bound="true">{0}鍙戣〃浜?Joe Scalise on 2005騫?8鏈?8鏃? 02:41 涓嬪崍 MDT
绔欑偣錛?http://www.einvite.com #
In addition, while there are many articles on authentication with JAAS [1], [2] , using the API for authorization is relatively undocumented [3]. This paper will show how to use JAAS to secure resources in an MVC architecture.
There are two points of integration with Struts. During the login process and when the client requests a resource from Struts (which will usually be a URL). At each of these points, the application should defer to JAAS classes to perform the action.
This paper will first examine the JAAS infrastructure and then explain how the integration outlined above took place.
Who should read this
This article is for developers who are relatively familiar with web applications and the java security framework. A sample application in Struts is used for the examples [4], but familiarity with Struts is not required. For information on java security, check out Java Security by Scott Oaks. For information on web applications, check out Sun's web site [5]. For information on Struts, check out the Struts web site [6].
Conventions
For the purposes of printing, text that should be on one line will be broken up into more. This will be indicated by a backslash (\) at the end of the line.
Authentication
Login Module
The login module receives information about the user and authenticates the user, thereby verifying that he or she is a valid subject. There are several implementations of login modules currently available. If a custom login module is needed, resources are available [8].
Login Module implementations
Sun provides default implementations for Solaris and NT.
Tagish has several implementations licensed under the GPL [9].
JBoss provides several implementations [10].
Note: For WebLogic 6.1 and Tomcat 3.2, both the jaas.jar and the login module classes needed to be in the non webapp specific classpath.
These login modules are identified by a name in a configuration file and then called by a LoginContext class that JAAS provides. The LoginContext class does not appear to be thread safe--the javadoc states that " a LoginContext should not be used to authenticate more than one Subject. A separate LoginContext should be used to authenticate each different Subject" [11]. Most of these modules expect to be run from an application or on the command line, and thus to be able to interact directly with a user. Since a web application resides on a server, it may be necessary to write an adapter to pull the needed user information from where the web application stores it and place it in a form that the login module can process.
Module Configuration
In addition to putting classes that can do authentication in the correct classpath location, some configuration is required to let the JAAS classes know what type of authentication is possible. JAAS allows complex login schemes [12], however, for most web applications, such complexity is not required. Typically, a username and password will be read from a form, and compared against known server side values. An authentication scheme which verifies a username and password combination against a server side file will be examined below.
Example 1. JAAS login module configuration file
FileLogin
{
com.tagish.auth.FileLogin required debug=true \
pwdFile="/usr/local/tomcat/webapps/struts-example/WEB-INF/passwd";
};
In Example 1, the FileLogin authentication scheme has one required module. FileLogin implemented by the com.tagish.auth.FileLogin class. Both the class name and a token indicating the relationship of the module to the scheme are mandatory. Here the required token indicates that the FileLogin module must validate this login or the scheme as a whole fails. It is also possible to pass additional information to the login module in this configuration file. This module needs to know where its password file is located.
Since this configuration file may be located anywhere on the file system, the authentication classes need to be informed where this file is. There are two ways to do this: adding parameters to a JVM wide general configuration file (java.security), or passing the information in on the command line when starting the JVM.
Setting Login Configuration Information Via java.security
This file is where the JVM looks for security related configuration parameters. It is typically located in the JAVA_HOME/jre/lib/security directory. It is possible to have an arbitrary number of login configuration files detailing any number of authentication schemes. These are "read and unioned into one single configuration" [13].
Example 2. java.security entry for login module configuration
login.config.url.1=file:${java.home}/lib/security/tagish.login
login.config.url.2=file:${java.home}/lib/security/struts.login
This entry specifies the location of files containing authentication scheme definitions. Also, note that java system properties may be referenced in java.security.
If the second line looked like login.config.url.3=file:${java.home}/lib/security/struts.login (note the 3), then struts.login would not be searched for login modules. Scanning begins at login.config.url.1 and continues to increment the suffix until no file is found. This is an implementation detail of the default Sun provided authentication classes. For more information, read about the Login Configuration Provider [14].
Setting Login Configuration Via the Command Line
It is also possible to set a java property on the command line when starting the JVM that tells it where to look for the JAAS login scheme configuration file. The property name is java.security.auth.login.config.
Example 3. Setting the location of the login scheme configuration file on the command line
$ java ... \
-Djava.security.auth.login.config==$JAVA_HOME/jre/lib/security/tagish.login ...
This command line overrides any previously set value for the login scheme configuration file location. In typical java fashion, if there was just one = in Example 3, an additional location for the login scheme configuration file would be specified.
Integrating Authentication Into Struts
After the login module is recognized by JAAS, hooks into the web application need to be written. In the example application, an Action class takes a username and password from its Form class and authenticates the user against a database. This is the logical place to hook in the JAAS authentication module.
The Struts example application has a LogonAction class. Initially, the relevant portion of this class looks like this (note that this code is unchanged except to break up lines for ease of printing).
Example 4. Initial LogonAction Authentication
125 String username = ((LogonForm) form).getUsername();
126 String password = ((LogonForm) form).getPassword();
127 Hashtable database = (Hashtable)
128 servlet.getServletContext().getAttribute( \
Constants.DATABASE_KEY \
);
129 if (database == null)
130 errors.add(ActionErrors.GLOBAL_ERROR,
131 new ActionError("error.database.missing"));
132 else {
133 user = (User) database.get(username);
134 if ((user != null) && !user.getPassword().equals(password))
135 user = null;
136 if (user == null)
137 errors.add(ActionErrors.GLOBAL_ERROR,
138 new ActionError("error.password.mismatch"));
139 }
On line 125 and 126, the username and password that were submitted on the form are extracted into local variables. The code looks for an authentication database and verifies its existence on lines 127-132. Line 134 is where the actual authentication occurs. If the user exists in the database and the password in the database is the same as the password entered on the form, then the user is authenticated.
This approach works just fine for a sample application. The LogonAction class does all of the authentication in the perform method. But the LogonAction class should be a thin layer deferring the actual work to business objects. In addition, switching the source users are authenticated against requires changes to this class since it is hard coded to expect a password and username. Also, as mentioned in the "Login Module" section, using the JAAS authentication module will be difficult, as most implementations expect some level of user interaction. Putting a business object between the Struts application classes and the JAAS classes can make life easier.
Example 5. Modified LogonAction Authentication
125 String username = ((LogonForm) form).getUsername();
126 String password = ((LogonForm) form).getPassword();
127 Hashtable database = (Hashtable)
128 servlet.getServletContext().getAttribute(Constants.DATABASE_KEY)
129 if (database == null)
130 errors.add(ActionErrors.GLOBAL_ERROR,
131 new ActionError("error.database.missing"));
132 else {
133 Auth fa = new com.xor.auth.FileAuth(username,password);
134 if (fa.authenticate()) {
135 user = (User) database.get(username);
136 HttpSession sess = request.getSession();
137 sess.setAttribute(Auth.SUBJECT_SESSION_KEY, fa.getSubject());
138 }
Lines 125-132 are the same in this example as they were in Example 4. However, rather than the LogonAction class performing the authentication, it defers to a special Auth class. Note that on line 135, the database is still used to get the User object, since the sample application used it in other code.
Auth is a custom interface specifying the contract that the adapter for the JAAS classes must fulfill to interface with web applications in general and this example application in particular. FileAuth is an implementation of that interface which adapts the com.tagish.auth.FileLogin login module specified in Example 1.
Calling the Tagish Authentication Module
The FileAuth class defers to the FileLogin class for almost all its functionality.
Example 6. The authenticate Method of the FileAuth Class
1 public boolean authenticate() {
2 try {
3 lc = new LoginContext("FileLogin", \
new MyCallBackHandler(username,password));
4 lc.login();
5 } catch (LoginException le) {
6 return false;
7 }
8 return true;
9 }
On line 3, the LoginContext object is told to look for the FileLogin authentication scheme, and passed a custom callback handler. Normally, callback handlers interact with the user and retrieve information needed for authentication, but the ActionForm class has already done this. In a way, the form is a "call forward" handler, because the request/response paradigm of the web does not fit well with the client/server architecture of which the callback handler is part. The problem is worked around by passing the needed information to the callback handler on instantiation.
On line 4, the LoginContext attempts to login the user via the modules found in the FileLogin authentication scheme. If the login fails for any reason, a LoginException will be thrown, otherwise flow continues normally. A Subject is instantiated by the login module and exposed as a property of the LoginContext object. The Subject is also populated with the Principals associated with that user by the login module.
Cache Subject in Session
After successfully authenticating the user, the Subject should be stored in the HttpSession. (The Subject is small; one Subject with one Principal serialized to a file 337 bytes in size.) This caching allows other parts of the application to perform authorization checks without requiring the user to login again. It also ensures that the other parts of the application are aware that the user has been authenticated, and logs out the user when the HttpSession expires. The storage process is shown on line 137 of Example 5; here the portion of the API of the login module that returns the newly authenticated Subject is examined. To retrieve the subject, the FileAuth class again defers to the LoginContext.
Example 7. The FileAuth Class Exposes the Subject
1 public Subject getSubject() {
2 if (lc == null) {
3 throw new IllegalStateException("either login failed or \
the authenticate method hasn't been called.");
5 } else {
6 return lc.getSubject();
7 }
8 }
The getSubject method can only be called after the authenticate method.
Of course, another option would be to store the username and password in the session, and re-authenticate every time. If the Subject were large and the authentication process was fairly fast, this option might be viable.
Authorization
Permissions
Permissions are the heart of authorization; they control access to resources. However, the JAAS permissions are built on top of the existing java security model. This model is very good for controlling access to resources like sockets and files, but has no concept of URLs. Thus, to apply JAAS to a web application, a new permission class must be created.
Create permission class
A custom Permission class that understands URLs must be created. There are two ways to do this.
Extending java.security.BasicPermission is one option. Using this would tie permissions to literal URLs (e.g, one could say that the admin principal should have access to the /admin/index.do page). However, the other option would be better: to create a URLPermission class extended the java.security.Permission class and handled wild cards in a manner similar to the java.io.FilePermission class [15]. Then, one could say that the admin principal should have access to the /admin/ directory and all resources below it.
In either case, since URLs are read only, there is no need for any of these permissions to have an action attribute. On the other hand, it may be useful to specify an attribute of a Permission to determine whether a given URL may only be viewed over a secure connection. For more on extending Permissions in novel manners, see the interesting IBM article titled Extend JAAS for class instance-level authorization [16].
However, since this is a proof of concept, a BasicPermission implementation is fine. For a production system, subclassing Permission would be required.
Access Control Policy Configuration Files
The default configuration for JAAS permissions looks much like the configuration for normal java security. Java security entries consist of a listing of permissions. The permissions can be limited to either a specific code base, or code that has been signed by a specific person, or both. JAAS permission listings can have none, one or both of these elements. However, at least one Principal association is required.
Example 8. Sample JAAS policy file
grant Principal * * {
permission com.xor.auth.perm.URLPermission \
"/struts-example/logoff.do";
};
grant Principal com.tagish.auth.TypedPrincipal "user" {
permission com.xor.auth.perm.URLPermission \
"/struts-example/editRegistration.do";
};
grant Principal com.tagish.auth.TypedPrincipal "admin" {
permission com.xor.auth.perm.URLPermission \
"/struts-example/editRegistration.do";
permission com.xor.auth.perm.URLPermission \
"/struts-example/adminMenu.do";
};
The first grant allows everyone to view the logoff.do resource. Anyone with the user Principal is allowed to view /struts-example/editRegistration.do.
At times there will be principals that are granted a superset of the permissions granted to other principals. For example, in Example 8, subjects with the admin principal can view all resources that subject with the user principal can. However, the /struts-example/editRegistration.do URL is listed twice (once in the user section and once in the admin section). It would be nice to be able to avoid duplicating permissions and delineating this relationship between principals, but this is not possible. While more than one principal can be listed for a given set of permissions, if that happens, "[t]he current Subject running the code must have all of the specified Principals in its Principal set to be granted the entry's Permissions" [17]. However, this problem may be dealt with by granting all users with the admin Principal the user Principal as well.
On the other hand, it is possible to have permissions be supersets of other permissions. The implies method of the Permission class can be overridden to provide this behavior. For example, there could be an admin Permission which could imply all other URL permissions.
Note: Example 8 is an example of a file based implementation of the policy. It canbe replaced with a RDBMS implementation by changing the value of the auth.policy.provider variable in the java.security file.
Since the policy file may be located anywhere, JAAS needs to be told where to look for it (this problem is similar to that faced by the authentication configuration setup detailed in Section 2.2. There are two ways to do this: adding parameters to a system wide general configuration file (java.security), or passing the information in as a command line option when starting the JVM.
Note: With WebLogic 6.1, only the command line method worked. Tomcat 3.2 seemed to work with both methods.
Setting the JAAS Policy File Location in the java.security file
This file is where the JVM looks for security related configuration parameters. It is typically located in the JAVA_HOME/jre/lib/security directory. It is possible to have arbitrary numbers of JAAS policy files. These are "read and unioned into one single policy" [18].
Example 9. java.security entry for permission policy configuration
auth.policy.url.1=file:${java.home}/lib/security/struts.policy
auth.policy.url.2=file:${user.home}/.struts.policy
This example should check the policy files in both places. Note also that java system properties can be referenced in the java.security file.
If the second line looked like auth.policy.url.3=file:${user.home}/.struts.policy (note the 3), then the .struts.policy file would not be searched for permission sets. Scanning starts at auth.policy.url.1 and increments the suffix by one until there is no file found. This is an implementation detail of the default Sun permission file parsing class. Obviously, an authorization scheme that used a database would have different behavior.
Setting the JAAS Policy File Location Via the Command Line
It is also possible to set a java property on the command line when starting the JVM that tells it where to look for the JAAS policy file. The property name is java.security.auth.policy.
Example 10. Setting the location of the JAAS policy file on the command line
$ java ... \
-Djava.security.auth.policy==$JAVA_HOME/jre/lib/security/struts.policy ...
This command line overrides any previously set location of the JAAS policy file. If there was only one =, the property would indicate an additional policy file path.
Integrating Authorization into Struts
Now that permissions have been set up and JAAS knows about them, it is time to actually apply them in the context of the application. Every java class that accesses a potentially security sensitive resource needs to check to see if the SecurityManager is running, and if so verify that the calling class is running in a context that allows the access.
Example 11. Typical permission check
Permission p = new FilePermission("/tmp/foo","read");
SecurityManager s = System.getSecurityManager();
if ( s != null) s.checkPermission(p);
The code here is trying to read /tmp/foo. If no SecurityManager is installed (typically via the -Djava.security.manager command line switch), System.getSecurityManager() returns null, and access does not need to be verified.
The web application needs to perform a similar check for each URL that the user is trying to view. In an MVC architecture, the logical place to put such access control is the controller. In Struts, the ActionServlet is the controller. For the sample application the controller class, org.apache.struts.action.ActionServlet, was subclassed, and the process method was overridden.
Installing Access Control Logic into the ActionServlet
First of all, it is important to note that when the ActionServlet is subclassed to provide authorization services, only resources that the ActionServlet controls are protected. Thus, in the example application, only Actions are protected. In a real application, it'd be better to have two methods of access control:
A central servlet which checks authorization before any URL is viewed. For example, all users may need to be logged in before viewing any pages. (If using the 1.3 version of the servlet specification, a filter might be a good choice.)
A JSP tag or other method which may be used to check to see if a user has a given principal before showing content fragments. For example, a menu item may only be accessible to users with the admin permission.
Both of these should delegate to a business class that does the actual security check.
There are at least two pages whose view should not be protected at all: the login page and the login error page. In addition, there are certain security attributes that are peculiar to web applications; for example, some users may need to have all interaction over an SSL connection. The controller needs to do at least three things to provide authorization services for a web application.
Recognize which pages are viewable by every authenticated user and respond accordingly.
Recognize which state, secure or non secure, a request is in and respond accordingly.
Check to see that a user is allowed to view a given URL and respond accordingly.
The ActionServlet Subclass
Example 12. The Overridden process Method
1 protected void process(HttpServletRequest request, \
HttpServletResponse response)
2 throws ServletException, java.io.IOException
3 {
4 String loginPage = request.getContextPath()+"/logon.do";
5 String pageReq = request.getRequestURI();
6 Permission perm = \
PermissionFactory.getInstance().getPermission(pageReq);
7 Subject subject = \
((Subject)(request.getSession().getAttribute( \
Auth.SUBJECT_SESSION_KEY)));
8 if (subject == null && \
(! request.getRequestURI().equals(loginPage))) {
9 // redirect to login page
10 } else if (subject == null && \
request.getRequestURI().equals(loginPage)) {
11 // login page is always permitted
12 super.process(request,response);
13
14 } else {
15 if ( ! AuthUtils.permitted(subject, perm) ) {
16 // subject is not permitted; redirect to error page
17 } else {
18 super.process(request,response);
19 }
20 }
21 }
Lines 6 and 7 get needed information including the Permission that represents the URL the user is trying to access (PermissionFactory is a factory class that returns the appropriate Permission), and the cached Subject.
On lines 8-14, the case of the unauthenticated user is handled by redirecting the user to the login page, where access is always allowed (line 12). The reason that this can't be handled by the wildcard grant in Example 8 is that the Subject is null in this case. If the program catches this and creates a new Subject, that Subject still has no Principals and thus is still denied access. For a production system, it's conceivable this case could be handled with an anonymous Principal.
If this is a request for any other resource than the login page, on line 15 the access control class is called. Access is either disallowed, or the process method of the superclass is called.
The ActionServlet should not know about the particulars of the access control; this should instead be handled by a business class. For this example, such a business class was written; it is examined in the next section.
The Access Control Class
This class is similar to the java.security.AccessController class [19], and ends defers to that class eventually (via the SecurityManager). The logic in the class can then be reused anywhere that authorization is needed. The class will basically check whether a Subject has a given Permission.
Example 13. An Abridged Permission Checking Class
1 package com.xor.auth;
2
3 import java.security.*;
4 import javax.security.auth.*;
5 import java.util.*;
6
7 public class AuthUtils {
8
9 static public boolean permitted(Subject subj, final Permission p) {
10 final SecurityManager sm;
11 if (System.getSecurityManager() == null) {
12 sm = new SecurityManager();
13 } else {
14 sm = System.getSecurityManager();
15 }
16 try {
17 Subject.doAsPrivileged(subj, new PrivilegedExceptionAction() {
18 public Object run() {
19 sm.checkPermission(p);
20 return null;
21 }
22 },null);
23 return true;
24 } catch (AccessControlException ace) {
25 return false;
26 } catch (PrivilegedActionException pae) {
27 return false;
28 }
29 }
30 }
The example has had all logging, error checking and comments removed for brevity. Production code should, for example, test to see that neither the Permission nor the Subject are null.
Lines 10-15 create a SecurityManager. If the application happens to be one of the rare java applications that run with a SecurityManager installed, then that SecurityManager should be used. If not, a new one is created to check the permissions on line 12.
Lines 16-23 do the actual permission check. If the Subject does not have a Principal which has been granted the Permission, the SecurityManager will throw an exception. For reasons of cleanliness and clarity, this exception is caught and converted to false, which is returned to the calling class. Otherwise, the action is allowed, and true is returned.
The null on line 22 is very important; it tells the SecurityManager to consider this resource access in an isolated context, ignoring the permissions of code currently on the execution stack. For further information, see chapter 5 of Java Security.
Note: If the application is running with a SecurityManager enabled, make certain that the AuthUtils class has been granted the doAsPrivileged AuthPermission in the standard java security policy file. Otherwise, this permission checker will not be able to run; a SecurityException will be thrown when line 17 is reached.
Conclusion
The authentication piece of JAAS seems fairly bulletproof. The idea of pluggable authentication modules is great and the developer can leverage a number of existing modules to ease development.
Using JAAS to leverage the SecurityManager for authorization is entirely commensurate with the java security model. There are resources that only certain users with certain principals should be able to see. Rather than reinvent an access control layer, it makes sense to use the one that java already provides.
However, there are some caveats. This was an extremely simplistic example, and the reader will have noted the number of places where parts of the system need to be replaced to create a production system; these include a new controller, permission class, and policy implementation. In addition, this permission model does not map well to the concept of different protocols used to view a URL.
Definitions
user: a real world entity. It can be another computer system or a human being. This is not represented by an object.
subject: the user as seen by the web application. Subject is the class that represents this concept.
principal: one view of the subject. The API states that it "represents the abstract notion of a principal, which can be used to represent any entity, such as an individual, a corporation, and a login id A Principal is a class that represents a principal" [7]. A principal can be thought of as a role or a group, but those terms have special meaning in J2EE (in the servlet container, for one).
resource: anything in a system to which unlimited access is not granted to all.
permission: access to a resource. The Permission class represents a triplet of resource, action and name. Permissions can be granted to a Principal.
Authentication: the act of verifying that a user is a subject and granting the user certain principals; "who you are."
Authorization: the act of verifying that a user is allowed to access a certain resource; "what you may do."
Authentication module: one method of authenticating a user. Examples include verifying against /etc/passwd and examining the contents of a cookie.
Authentication scheme: a combination of authentication modules.
Current instructions for configuring Tomcat for JAAS-based Windows authentication.
This is the solution for my previous post.
Samba NTLM Authentication: (Get it here.) This package configures exactly as described. I used the example servlet (NtlmHttpAuthExample) included with the package and set up the filter as in the docs. The web.xml file is here.
Notes: I tested on my home (windows) network which does not have a domain controller; I used workgroup authentication and did not test against a PDC. So YMMV.
There are two main drawbacks of this approach:
The first is that the jcifs filter (as currently implemented) does not return group membership information. If your servlet app uses security roles and security constraints, this is a problem since you need some way of getting a user鈥檚 group memberships (so the container can map them to security roles). You could get probably around this problem by extending the jcifs library to return group information; this is more work than I planned to invest. Another approach would be to create a second filter that maps user names to group memberships, perhaps using the tomcat-users.xml file. This would be cumbersome and proprietary, but it would probably work.
The second drawback is that NTLM authentication is deprecated in the Windows world. Windows 2000 and 2003 use Kerberos authentication, and only fall back on NTLM when they have to.
For these reasons, I abandoned the Samba NTLM approach and looked at the Tagish / JAAS approach.
JAAS with Tagish SSPI JAAS provider: JAAS with the Tagish SSPI-based login module is the way to go. The Tagish login module is based on the Windows SSPI API, which provides an authentication service for distributed environments using the best available protocol; i.e. it uses Kerberos when that is available and transparently falls back on NTLM when Kerbos is not available. In addition, SSPI returns the group membership information, which is necessary for servlet apps that use security roles and security constraints.
The first step in setting this up is to configure the Tagish login module according to the instructions that come with it. In a nutshell, the steps are as follows:
You can test the configuration using the login.bat file that comes with the Tagish package. If you are on a workgroup, you may need to edit a registry setting to get network authentication to work properly. (I had to.) See this Microsoft KB article for more information; apparently, Windows XP has a 鈥淔orceGuest鈥?parameter that forces all network logins for a workgroup to use the guest account.
The second step is to configure Tomcat to use this login module. Here are the steps I used to configure it in Tomcat 5.0.16. Note that the first 4 steps are the same ones for setting up the Tagish login module, just tweaked a little for Tomcat.
The most important thing to remember is that the userClassNames and roleClassNames have to be exactly the same names as the classes that the Tagish login module returns. This is necessary because the Tomcat JAASRealm class compares the classnames of the principal objects with the class names given in the realm configuration. If they do not match exactly, they are ignored.
You can test the configuration using the same NtlmHttpAuthExample servlet that we used to test Samba NTLM. You need a different web.xml that uses security roles; the one I used is here. For this to work, you have to login using an account that is a member of the TESTGROUP group.
To debug it, I had to get log4j working with Tomcat5, which is an adventure in itself. It turns out that the instructions in the Tomcat5 FAQ are incomplete; you need to add commons-logging.jar to common/lib in addition to all the other steps. If you run into trouble, getting log4j debug messages out of the org.apache.catalina.realm package should tell you what is going wrong.
JAASLogin { com.tagish.auth.DBLogin required dbDriver="sun.jdbc.odbc.JdbcOdbcDriver" dbURL="jdbc:odbc:DBLogin"; };灝?Principal 璁劇疆涓?com.tagish.auth.TypedPrincipal ( 濡傛灉鏄?NTSystemLogin 鍒欒緗負(fù) com.tagish.auth.win32.NTPrincipal ). 鎺ョ潃, 浣犵殑鏁版嵁搴撻渶瑕佷竴浜涜〃,
Login2 { sample.SampleLoginModule required; com.sun.security.auth.module.NTLoginModule sufficient; com.foo.SmartCard requisite debug=true; com.foo.Kerberos optional debug=true; };鍋囧鎴戜滑鐢?Login2 鐨勮繖縐嶇櫥闄唖tack, 鍙互璁劇疆 required, sufficient, requisite, optional 鍥涚鐘舵?
Login2 楠岃瘉鐨勫悇縐嶇姸鍐靛垪琛?/B> | |||||||||
---|---|---|---|---|---|---|---|---|---|
SampleLoginModule | required | pass | pass | pass | pass | fail | fail | fail | fail |
NTLoginModule | sufficient | pass | fail | fail | fail | pass | fail | fail | fail |
SmartCard | requisite | * | pass | pass | fail | * | pass | pass | fail |
Kerberos | optional | * | pass | fail | * | * | pass | fail | * |
Overall Authentication | pass | pass | pass | fail | fail | fail | fail | fail |
JAASLoginModule { com.tagish.auth.DBLogin required dbDriver="net.sourceforge.jtds.jdbc.Driver" dbURL="jdbc:jtds:sqlserver://localhost:1433/JAASDB" dbUser="sa" dbPassword="sa_password" userTable="Users" roleTable="Roles" roleMapTable="RoleMap"; };鍦?tomcat 鍚姩鑴氭湰涓姞鍏?BR>顧☆棸 JAVA_OPTS=-Djava.security.auth.login.config==C:\tomcat4\conf\login.config 騫朵笖淇敼 server.xml, 璁?appName 鍘誨搴?login.config 涓殑 JAASLoginModule, 浠ュ強(qiáng)璁劇疆 User/Group 鐩稿叧鐨?Principal
<Realm className="org.apache.catalina.realm.JAASRealm" appName="JAASLoginModule" userClassNames="com.tagish.auth.TypedPrincipal" roleClassNames="com.tagish.auth.TypedPrincipal" debug="99"/>鏈鍚庤緗?web.xml 涓殑 login-config 浠ュ強(qiáng)瑕佷繚鎶ょ殑鏁版嵁, 渚嬪
<security-constraint> <web-resource-collection> <web-resource-name>User Protected</web-resource-name> <url-pattern>/protected/*</url-pattern> <url-pattern>/protected.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>user</role-name> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>MyJAASRealm</realm-name> </login-config>SECTION 06 緇撹
鍦ㄤ換浣曚竴縐峎EB搴旂敤寮鍙戜腑錛屼笉璁哄ぇ涓皬瑙勬ā鐨勶紝姣忎釜寮鍙戣呴兘浼?xì)閬囧堫C竴浜涢渶瑕佷繚鎶ょ▼搴忔暟鎹殑闂錛屾秹鍙?qiáng)鍒扮敤鎴风殑LOGIN ID鍜孭ASSWORD銆傞偅涔堝浣曟墽琛岄獙璇佹柟寮忔洿濂藉憿錛熷疄闄呬笂錛屾湁寰堝鏂瑰紡鏉ュ疄鐜般傚湪鏈枃閲岋紝鎴戜滑涓嶄細(xì)鎶婃墍鏈夌殑楠岃瘉鏂規(guī)硶閮借冭檻鍒幫紝鎴戜滑鐨勭洰鐨勬槸璁╀綘瀛︿細(xì)濡備綍浠ユ渶綆鍗曟渶鏂逛究鐨勯獙璇佹柟娉曟潵瀹屾垚銆備笅闈㈠皢璁ㄨ鍩烘湰鐨勶紙BASIC錛夊拰鍩轟簬琛ㄥ崟鐨勶紙F(tuán)ORM-BASED錛夐獙璇佹柟寮忋傛垜浠冭檻浣跨敤TOMCAT浣滀負(fù)WEB SERVER錛屽畠閫氳繃server.xml鍜寃eb.xml鏂囦歡鎻愪緵鍩烘湰鐨勫拰鍩轟簬琛ㄥ崟鐨勯獙璇併侸SP欏甸潰涓殑j_security_check 琛ㄥ崟(for FORM-based) 闇瑕佷袱涓弬鏁幫細(xì)j_username鍜宩_password銆傚畠浠寚瀹氫簡(jiǎn)鍦⊿QL鏁版嵁搴撲腑鐨勭櫥闄嗚鑹層備綘鑳藉鐪嬪埌錛屽畠鐨勫脊鎬у寲錛屽彲鐢ㄦу拰蹇呰鎬с?/P>
絎竴姝ワ紝鎴戜滑瑕佷笅杞絋OMCAT鍜孧YSQL錛屽墠鑰呯敤鏉ュ仛WEB SERVER錛屽悗鑰呯敤鏉ュ仛SQL SERVER銆傝繕瑕佷笅杞絁DBCRealm宸ュ叿錛屽畠鍦═OMCAT涓嬌鐢紝鐢ㄦ潵鍋歁YSQL榪炴帴鍣紝榪炴帴MYSQL鏁版嵁搴撶殑銆?/P>
鎴戜滑鍋囪浣犲凡緇忓畨瑁呬簡(jiǎn)TOMCAT鍜孧YSQL錛岄偅涔堟垜浠紑濮嬩粠SERVER鐨勯厤緗叆鎵嬩簡(jiǎn)銆傚綋鐒?dòng)灱屼綘杩橀渶瑕佸畨瑁匤AVA鐨凪YSQL榪炴帴椹卞姩錛屾垜寮虹儓寤鴻鍙嬌鐢ㄧǔ瀹氱殑椹卞姩鐗堟湰錛屽洜涓哄湪鏈変簺鎯呭喌涓嬶紝alpha/beta鐗堟湰鐨勯┍鍔ㄤ笉鑳芥甯稿伐浣溿?/P>
涓嬮潰鎴戜滑鏉ユ搷浣淪QL鏁版嵁搴撱傝佸疄璇達(dá)紝MYSQL鍜孴OMCAT鏄浉褰撳ソ鐨勫伐鍏鳳紝瀹冧滑閮芥槸璺ㄥ鉤鍙扮殑錛屼笉綆′綘鐨勬搷浣滅郴緇熸槸WINDOWS榪樻槸綾諱技UNIX/LINUX鐨勶紝瀹冧滑閮借兘姝e父榪愯銆傚洜姝わ紝涓嶈榪愯鐜錛屽畠浠殑閰嶇疆榪囩▼閮芥槸緇濆涓鏍風(fēng)殑銆?/P>
MySQL
鍦ㄥ懡浠よ涓墽琛宮ysql 瀹㈡埛绔懡浠わ紝鐒跺悗杈撳叆錛?/P>
create database weblogin;
榪欎釜灝嗕負(fù)浣犲垱寤轟竴涓獁eblogin鏁版嵁搴擄紝瀹冨皢淇濆瓨鐢ㄦ埛鍚嶅拰瀵嗙爜浠ュ強(qiáng)瑙掕壊絳変竴鍒囦俊鎭備綘瀵規(guī)暟鎹簱鎵鍋氱殑浠諱綍鏀瑰彉閮戒細(xì)鐩存帴绔嬪嵆鍙嶆槧鍑烘潵銆傛瘮濡傝娣誨姞鐢ㄦ埛錛屾敼鍙樼敤鎴峰瘑鐮佸拰瑙掕壊絳夈?/P>
create table users (
login varchar (15) not null,
pass varchar (15) not null,
primary key (login)
);
鎴戜滑鍒涘緩涓涓猽sers琛ㄧ敤鏉ヤ繚瀛樼敤鎴風(fēng)殑LOGIN鍜孭ASSWORD錛?/P>
create tables groups (
login varchar (15) not null,
group varchar (15) not null,
primary key (login, group)
);
濡備綘鐪嬪埌鐨勶紝鎴戜滑瑕佸湪group琛ㄩ噷淇濆瓨login灞炰簬鍝釜group鐨勪俊鎭備笅闈紝鎴戜滑瑕佹彃浜庝竴浜涙暟鎹敤鏉ユ祴璇曚嬌鐢紝騫跺畬鎴怣YSQL鐨勯厤緗伐浣滐細(xì)
insert into users ('green', 'testpwd');
insert into groups ('green', 'testgroup');
鐜板湪錛屾垜浠垱寤轟簡(jiǎn)涓涓敤鎴峰彨green錛屼粬鐨勫瘑鐮佹槸testpwd錛屼粬灞炰簬testgroup榪欎釜鐢ㄦ埛緇勩傛帴鐫錛岃疆鍒癟OMCAT鐨勯厤緗簡(jiǎn)銆?/P>
Tomcat
TOMCAT鏈韓騫舵病鏈夎兘鍔涙搷浣滄暟鎹簱鏉ュ疄鐜拌韓浠介獙璇併備絾鏄彲浠ヤ緷闈燡DBCRealm銆備笅闈㈡垜浠潵浣跨敤瀹冦?/P>
涓嬮潰鎴戜滑浠嶵OMCAT鐨刓conf\server.xml鏂囦歡鏉ュ紑濮嬫垜浠殑閰嶇疆銆傛墦寮榪欎釜鏂囦歡騫舵壘鍒頒笅闈㈢殑鍐呭:
<Realm className="org.apache.catalina.realm.MemoryRealm" />
鍒犻櫎榪欎竴琛岋紝鎴栬呯敤<!-- ... --> 娉ㄩ噴鎺夊畠,鎴戜滑瑕佷嬌鐢↗DBCRealm銆傛墍浠ヨ緭鍏ヤ笅闈㈢殑鍐呭:
<Realm className="org.apache.catalina.realm.JDBCRealm" debug="99"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://localhost/weblogin?user=test&password=test"
userTable="users" userNameCol="login" userCredCol="pass"
userRoleTable="groups" roleNameCol="group" />
涓嬮潰鎴戜滑瀵筬ield鍙傛暟鍋氳緇嗚瑙?
debug鈥旇繖鏄垜浠緗殑debug鍙傛暟錛屾暟瀛楄秺楂樻樉紺轟俊鎭秺璇︾粏銆?
driverName鈥旇繖涓槸MYSQL椹卞姩鐨勫悕瀛椼傝紜繚榪欎釜椹卞姩鐨凧AR鍖呭湪TOMCAT鐨凜LASSPATH涓兘澶熸壘鍒板畠銆?
connectionURL鈥旇繖涓槸鐢ㄦ潵寤虹珛JDBC榪炴帴鐨勬暟鎹簱URL銆傚湪榪欎釜field閲岋紝weblogin鏄垜浠暟鎹簱鐨勫悕瀛椼倁ser鍜宲assword 鏄垜浠櫥闄嗘暟鎹簱鐨勭敤鎴鋒暟鎹?
userTable鈥斾竴涓畾涔夋湁userNameCol鍜寀serCredCol瀛楁鐨勮〃銆?
userNameCol鍜寀serCredCol鈥攗sers琛ㄩ噷瀹氫箟鐨刲ogin鍜宲ass銆?
鐜板湪錛屾垜浠畬鎴愪簡(jiǎn)閰嶇疆榪囩▼銆備笅闈紝鎴戜滑瑕侀厤緗甒EB搴旂敤紼嬪簭鏉ヨ榪欐牱涓涓韓浠介獙璇佹柟寮忎繚鎶よ搗鏉ャ傛垜浠涓句袱涓緥瀛愩傛渶綆鍗曠殑鏄熀鏈韓浠介獙璇佹柟寮忥紝鐒跺悗灝辨槸鍩轟簬琛ㄥ崟鐨勮韓浠介獙璇併傚湪絎竴縐嶆儏鍐甸噷錛屾垜浠皾璇曡闂彈淇濇姢鐨勬暟鎹紝灝嗕細(xì)鏈変竴涓狿OP-UP紿楀彛寮瑰嚭鎻愮ず浣犺緭鍏ヤ綘鐨刲ogin鍜宲assword銆傚湪絎簩縐嶆儏鍐甸噷錛屾垜浠細(xì)閫氳繃欏甸潰鐨勬柟寮忔潵璁╀綘閫氳繃韜喚楠岃瘉銆傝繖涓〉闈㈢殑鍐呭鍙互鏄換鎰忕殑錛岃繖涓彇鍐充簬浣犺浣跨敤鎬庝箞鏍風(fēng)殑楠岃瘉鏂瑰紡浜?jiǎn)銆?/P>
鍩烘湰韜喚楠岃瘉鏂瑰紡錛圔ASIC authorization method錛?BR>鎴戜滑鍋囪搴旂敤紼嬪簭鍦═OMCAT鐨刓webapps\webdemo, 鎴戜滑瑕佷繚鎶ゆ墍鏈夊湪admin 瀛愮洰褰曢噷鐨勬枃浠躲傛垜浠繀欏繪墦寮瀹冪殑\webapps\webdemo\WEB-INF\web.xml鏂囦歡錛岃緭鍏ヤ笅鍒楀唴瀹?
<security-constraint>
<web-resource-collection>
<web-resource-name>Web Demo</web-resource-name>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>testgroup</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Web Demo</realm-name>
</login-config>
璁╂垜浠潵鐪嬬湅鍒氭墠杈撳叆鐨勫唴瀹廣傛垜浠負(fù)搴旂敤紼嬪簭鍒涘緩浜?jiǎn)涓涓獁eb-resource-name騫舵槧灝勫埌l(fā)ogin-config 銆傛垜浠繕瀹氫箟浜?jiǎn)url-pattern, 瀹冩寚鏄庝綘鍙椾繚鎶ょ▼搴忕殑璺緞銆傚湪login-conf涓紝鎴戜滑瀹氫箟浜?jiǎn)涓涓狟ASIC auth-method銆?/P>
寰堢畝鍗曪紝瀵瑰悧錛熶笉瑕佸繕璁頒簡(jiǎn)錛屽湪浣挎敼鍙樼敓鏁堝墠瑕佸仠姝㈠茍閲嶅惎TOMCAT銆?/P>
琛ㄥ崟韜喚楠岃瘉鏂瑰紡錛團(tuán)ORM-based authorization method錛?BR>瀵逛簬榪欑鏂瑰紡錛屾垜浠粎浠呭彧闇瑕?
淇敼\webapps\webdemo\WEB-INF\web.xml
鍒涘緩涓涓櫥闄嗙敤鐨凧SP欏甸潰, 鐢ㄦ埛灝嗗湪榪欓噷鐨凥TML琛ㄥ崟涓緭鍏ヤ粬鐨勭櫥闄咺D鍜屽瘑鐮?
鍒涘緩涓涓狫SP error欏甸潰錛屼竴鏃﹂獙璇佸け璐ワ紝鐢ㄦ埛灝嗚煩鍒拌欏甸潰
濡傛灉浣犲厛灝濊瘯浣跨敤BASIC楠岃瘉鏂瑰紡錛屼綘鍙渶瑕佹敼鍙榣ogin-config 涓轟笅闈㈤偅涓孌典唬鐮併傚惁鍒欙紝浣犻渶瑕佽緭鍏ecurity-constraint浠g爜孌點(diǎn)備嬌鐢ㄤ笅闈㈢殑login-config:
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Web Demo</realm-name>
<form-login-config>
<form-login-page>/admin/login.jsp</form-login-page>
<form-error-page>/admin/error.jsp</form-error-page>
</form-login-config>
</login-config>
鎴戜滑璁劇疆琛ㄥ崟鐨刟uth-method騫跺畾涔変簡(jiǎn)form-login-config銆傝繖涓皢浣垮緱TOMCAT浣跨敤 \admin\login.jsp欏甸潰鏉ヨ鐢ㄦ埛鐧婚檰錛屼嬌鐢╘admin\error.jsp欏甸潰鏉ュ鐞嗙櫥闄嗗け璐ャ?/P>
浣犲彲浠ヤ嬌鐢ㄤ換浣曚綘鎯寵鐨勫嚭閿欎俊鎭傞〉闈㈠敮涓闇瑕佺殑灝辨槸涓嬮潰鐨凥TML琛ㄥ崟鏍囩錛屼綘瑕佹彃鍒伴〉闈腑錛?/P>
...
<form method="POST" action="j_security_check">
<input type="text" name="j_username">
<input type="text" name="j_password">
<input type="submit" value="Log in">
</form>
...
甯冨眬錛岄鏍鹼紝鎴栧叾浠栨墍鏈変綘鍠滄鐨勩傝繖涓猠rror欏甸潰鑳藉鍋氭垚浠諱綍浣犳兂瑕佺殑銆傛棤闈炲氨鏄氱煡鐢ㄦ埛錛岄獙璇佸け璐ヤ簡(jiǎn)銆?/P>
OK錛屽叏閮ㄥ畬鎴愪簡(jiǎn)銆備綘闇瑕佸仠姝㈠拰閲嶅惎涓閬峊OMCAT浣垮緱鏀瑰彉鐢熸晥銆?/P>
漏 Olexiy Prokhorenko, http://www.7dots.com/resume/
Co-author: Alexander Prohorenko
鍐呭瓨鍩燂細(xì)MemoryRealm 浠嶺ML鏂囦歡涓鍙栧畨鍏ㄩ獙璇佷俊鎭茍瀛樺叆鍐呭瓨?shù)腑銆?BR>JDBC鍩燂細(xì)JDBCRealm 閫氳繃JDBC椹卞姩紼嬪簭璁塊棶瀛樻斁鍦ㄦ暟鎹簱涓殑淇℃伅銆?BR>鏁版嵁婧愬煙錛欴ataSourceRealm 閫氳繃JDBC鏁版嵁婧愯闂瓨鏀懼湪鏁版嵁搴撲腑鐨勪俊鎭?BR>JNDI鍩燂細(xì)JNDIRealm 閫氳繃JNDI provider璁塊棶瀛樻斁鍦ㄥ熀浜嶭DAP鐨勭洰褰曟湇鍔″櫒涓殑瀹夊叏楠岃瘉淇℃伅銆?/P>
璁劇疆璧勬簮瀹夊叏綰︽潫
鍦╳eb.xml涓姞鍏?lt;security-constraint>鍏冪礌
<security-constraint>
銆<display-name>MZT</display-name>
銆<web-resource-collection>
銆銆銆<web-resource-name>protected test</web-resource-name>
銆銆銆<url-pattern>/test/*</url-pattern>
銆銆銆<http-method>POST</http-method>
銆銆銆<http-method>GET</http-method>
銆</web-resource-collection>
銆<auth-constraint>
銆銆<role-name>mztadmin</role-name>
銆</auth-constraint>
</security-constraint>
<login-config>
銆<auth-method>BASIC</auth-method>
銆<realm-name>test realm</realm-name>
</login-config>
璁劇疆JDBC鍩?/P>
<Realm className="org.apache.catalina.realm.JDBCRealm" debug="99"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://localhost/mzt"
connectionName="root" connectionPassword=""
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name" />
<Realm className="org.apache.catalina.realm.DataSourceRealm" debug="99"
dataSourceName="jdbc/tomcatusers"
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name"/>
鍦∕ySQL涓墽琛屼互涓婼QL璇彞:
#########
#鐢ㄦ埛琛?BR>create table users(
user_name varchar(15) not null primary key,
user_pass varchar(15) not null
);
#鐢ㄦ埛瑙掕壊琛?BR>create table user_roles(
user_name varchar(15) not null,
role_name varchar(15) not null,
primary key(user_name, role_name)
);
insert into users values('mzt','test');
insert into user_roles values('mzt','mztadmin');
BASIC and FORM-based Authorization in Your Web Application
By Olexiy & Alexander Prokhorenko
In the development of any, more-or-less big Web application, every developer collides at times with the problem of how to bear certain parts of the application in the protected area and to divide access to them by login and password. How do you carry out authentication? Actually, there are a lot of variants. In this article, we do not present a problem to consider all possibilities; our purpose is to learn how to work with the simplest yet rather convenient method of authorization. We will talk about BASIC and FORM-based authorizations. As a Web server, we will consider Tomcat, which provides BASIC and FORM-based authentication through server.xml and web.xml files; the use of a j_security_check form (for FORM-based) in a JSP page that requires two parameters j_username and j_password; and specifying roles (groups) within the SQL database. As you can see, it's a flexible, useful, and necessary set of capabilities.
To begin with, you need to download Tomcat, which we will use as a Web server and MySQL, which we will use as a SQL server. Also, you need to download the JDBCRealm tool which will be used with Tomcat, and the MySQL Connector/J to use with MySQL.
We assume you have installed Tomcat and MySQL properly, so we can start right from the server's configuration. Of course, you also need to install the MySQL Connector/J driver, and I strongly recommend using only stable releases of the driver because, in some cases, alpha/beta versions of the driver do not work in the given sheaf.
First of all, we will work with the SQL database. Honestly speaking, MySQL, as well as Tomcat, is pretty universal, and doesn't depend on the OS in which you are using it (Windows or Unix-like system), so the process of configuration will be absolutely the same; it doesn't matter where you run it.
MySQL
Execute the mysql client from the installation binary directory and type:
create database weblogin;
This will create the weblogin database in which we will keep user names, passwords, roles?everything. Thus, any changes you have made to the database directly (new users, changed passwords or roles, and so forth) will be reflected immediately.
create table users (
login varchar (15) not null,
pass varchar (15) not null,
primary key (login)
);
We will keep the user's login and password in this users table.
create tables groups (
login varchar (15) not null,
group varchar (15) not null,
primary key (login, group)
);
As you can see, we will keep information about which login belongs to which group in this groups table. Let's fill our tables with some test data and finish the process of MySQL configuration:
insert into users ('green', 'testpwd');
insert into groups ('green', 'testgroup');
So, we created the user green with the password testpwd in the group testgroup. And now, it's Tomcat's turn to be configured.
Tomcat
Tomcat itself has no ability to work with the database to carry out authentication. However, there is JDBCRealm for these purposes; we are going to use that.
We will start our configuration from Tomcat's \conf\server.xml file. Open this file and find the following string:
<Realm className="org.apache.catalina.realm.MemoryRealm" />
Remove this line or just comment it by using <!-- ... --> Instead of it, we will use JDBCRealm. Type the following:
<Realm className="org.apache.catalina.realm.JDBCRealm" debug="99"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://localhost/weblogin?user=test&password=test"
userTable="users" userNameCol="login" userCredCol="pass"
userRoleTable="groups" roleNameCol="group" />
We will consider all mentioned fields in a bit more detail:
debug?Here, we set the debug level. A higher number generates more detailed output.
driverName?The name of our MySQL driver. You need to be sure that the driver's JAR file is located in Tomcat's CLASSPATH.
connectionURL?The database URL that is used to establish a JDBC connection. In this field, weblogin is the name of our database; user and password are login data with which you are connecting to the database. In MySQL, such a user is created by default, so you can use it. In case you don't have such a user, you need to create your own user and make it capable of working with your weblogin database.
userTable?A table with at least two fields, defined in userNameCol and userCredCol.
userNameCol and userCredCol?The fields with the name of login field from the users table and pass.
Now, we are at the stage of finishing the configuration process. We need to configure your Web application to be protected with such an authentication. Below, we show examples of two configurations. The simplest is a BASIC authentification method, and a little more original method is a FORM-based one. In the first case at attempting to access the protected area, a pop-up window will appear with the requirement to enter your login and password. In the second case, we will get a page on which we will pass authentification on our defined JSP. The contents of a page can be anything; it should meet only few simple requirements on the contents of a HTML <form> tag. It is up to you what authorization methods you will use.
Basic authorization method
Let's assume that our Web application is located in Tomcat's \webapps\webdemo, and we need to protect all files placed in the admin subdirectory. We need to open its \webapps\webdemo\WEB-INF\web.xml file and type the following text:
<security-constraint>
<web-resource-collection>
<web-resource-name>Web Demo</web-resource-name>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>testgroup</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Web Demo</realm-name>
</login-config>
Let me say a few words about what we just did. We created web-resource-name for our application and mapped login-config to this resource. We defined url-pattern, which has information about which sub-directory of our entire application will be protected, and which role-name is allowed to access the protected area. In login-conf, we defined a BASIC auth-method.
Pretty easy, isn't it? Do not forget to stop and re-start Tomcat to make these our changes work.
FORM-based authorization method
For this method, we will only need to:
Modify \webapps\webdemo\WEB-INF\web.xml
Create a login JSP page, on which the user will get a HTML form to enter his login and password
Create a JSP error page that the user will get if an error happened during authorization
So, let's start from the very beginning. In case you tried the BASIC authorization method first, you need just to change the login-config section to the one listed below. Otherwise, you need to type the security-constraint section from the BASIC method (it's absolutely the same), but use the following login-config:
<login-config>
<auth-method>FORM</auth-method>
<realm-name>Web Demo</realm-name>
<form-login-config>
<form-login-page>/admin/login.jsp</form-login-page>
<form-error-page>/admin/error.jsp</form-error-page>
</form-login-config>
</login-config>
We set the FORM's auth-method and defined the form-login-config section; this will force Tomcat to use the \admin\login.jsp page as the page with the HTML form for the user to sign in, and use \admin\error.jsp in case the login failed.
You can have any login and error screen you like; the only requirement is that HTML <form> should be the following (to be more exact, it should have fields defined as such):
...
<form method="POST" action="j_security_check">
<input type="text" name="j_username">
<input type="text" name="j_password">
<input type="submit" value="Log in">
</form>
...
The layout, styles, or whatever else could be anything you like. The error page could be anything you want; you will need to inform the user that there that something is wrong with the authentication.
That is all. You need to stop and re-start Tomcat to make these changes work.
漏 Olexiy Prokhorenko, http://www.7dots.com/resume/
Co-author: Alexander Prohorenko
--------------------------------------------------------------------------------
By lanf
搴旂敤TOMCAT鍩轟簬JDBC鐨勭殑Realm 錛?錛?
浣滐紙璇戯級(jí)鑰咃細(xì)Lanf From LinuxAID
Realm鏄竴涓敤鎴鋒暟鎹簱鐨勬蹇碉紝綾諱技浜嶶nix涓殑鐢ㄦ埛緹ょ粍錛屽畠閫氳繃鐢ㄦ埛鍚嶅拰瀵嗙爜鏉ユ爣璇嗕竴涓敤鎴鳳紝榪欎釜鐢ㄦ埛灞炰簬涓瀹氱殑瑙掕壊錛坮ole錛夈傝屼竴涓壒孌婄殑web搴旂敤璧勬簮錛屽彲浠ラ檺瀹氭煇涓鑹茬殑鐢ㄦ埛鎵嶈璁稿彲璁塊棶銆傝繖縐嶈鍙瓥鐣ワ紝浣垮緱web搴旂敤鐨勬暣浣撴潈闄愭帶鍒朵笌搴旂敤緇嗚妭鐩稿墺紱伙紝浠庤岃幏寰楁洿濂界殑鍙厤緗с備笅闈㈡垜浠氳繃姣旇緝甯歌鐨勫熀浜庢暟鎹簱錛屼嬌鐢ㄧ洿鎺DBC榪炴帴鐨凴ealm鐨勯厤緗嬌鐢ㄦ儏鍐碉紝鐪嬬湅瀹冩槸濡備綍瀹炵幇綺楃矑搴︾殑ACL鐨勩?
JDBCRealm
JDBCRealm鏄嬌鐢↗DBC榪炴帴鍏崇郴鏁版嵁搴撶殑涓涓猅omcat 4 Realm鎺ュ彛鐨勫疄鐜般傚畠鍙互鐩存帴浣跨敤浣犵幇鏈夌殑鐢ㄦ埛鏁版嵁搴撹〃錛屾潵鑾峰彇瑙掕壊鐢ㄦ埛鐨勪俊鎭紝瀹屾垚楠岃瘉銆備綘蹇呴』婊¤凍浠ヤ笅鏉′歡錛?/P>
蹇呴』鏈変釜鏈夋晥鐨勬暟鎹〃錛岄噷闈㈡湁鎵鏈変綘闇瑕侀氳繃Realm鏉ヨ璇佺殑鐢ㄦ埛銆傝繖寮犺〃蹇呴』鑷沖皯鏈変袱涓瓧孌碉紝鍙互鐢ㄦ潵鏍囩ず鐢ㄦ埛鍚嶅拰瀵嗙爜銆?
闇瑕佹湁涓寮犺〃鏉ユ爣鏄庣敤鎴蜂笌瑙掕壊鐨勫搴斿叧緋伙紝鐢ㄦ埛鍙互鏈変換鎰忎釜瑙掕壊錛屾病鏈夎鑹蹭篃鏄悎娉曠殑錛岃繖鏄拰UNIX鐢ㄦ埛緹ょ粍鐨勪笉鍚屼箣澶勩傚悓鏍瘋繖涓〃涔熼渶瑕佷袱涓瓧孌碉紝鏉ユ槧灝勭敤鎴峰悕涓庤鑹插悕鐨勫搴斿叧緋匯?
鏁版嵁搴撳噯澶?
鍦ㄦ垜浠殑渚嬪瓙涓紝鎴戜滑寤轟袱寮犳柊琛ㄦ潵澶勭悊realm鐨勮璇併?
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)
);
JDBC椹卞姩
浣犻渶瑕佸皢浣犵殑JDBC鍚姩鍖呮斁鍦?$CATALINA_HOME/server/lib 鐩綍鎴栬?$CATALINA_HOME/common/lib 鐩綍涓嬶紝紜繚Tomcat鑳介氳繃CLASSPATH鎵懼埌瀹冦備嬌鐢╩ysql鏁版嵁搴撶殑璇濓紝浣犲彲浠ヤ嬌鐢ㄧ被浼?mm.mysql-2.0.4-bin.jar 鐨勯┍鍔ㄥ寘錛汷racle 9i浣犲彲浠ヤ嬌鐢╫jdbc14.jar絳塐racle鑷甫鐨勯┍鍔紱PostgreSQL鍙互鍦?A >http://jdbc.postgresql.org/ 鍙栧緱鍚堥傜殑椹卞姩紼嬪簭銆?
緙栬緫server.xml
緙栬緫$CATALINA_HOME/conf/server.xml鏂囦歡錛屽湪host閲屾坊鍔犲涓嬬墖孌碉紙浠ySQL涓轟緥錛?
<Realm className = 'org.apache.catalina.realm.JDBCRealm' debug='0'
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'/>
鍏朵腑 jdbc:mysql://localhost/authority?user=dbuser&password=dbpass 鏄疢ysql鐨勮繛鎺ヤ覆錛屼綘鍙互鏍規(guī)嵁浣犵殑闇瑕佽繘琛屼慨鏀廣傚叾鏈夊叧灞炴т粙緇嶅涓嬶細(xì) 灞炴?鎻忚堪
className Realm鐨勫疄鐜扮被錛岃繖閲屽繀欏繪槸 'org.apache.catalina.realm.JDBCRealm'
connectionName 鏁版嵁搴撶敤鎴峰悕
connectionPassword 鏁版嵁搴撶敤鎴風(fēng)殑瀵嗙爜
connectionURL 鏁版嵁搴撶殑JDBC榪炴帴涓?BR>
debug Debug鐨勭▼搴︼紝瀹冨拰Logger鐩稿叧閰嶇疆閰嶅悎浣跨敤錛屽艱秺楂樹(shù)俊鎭秺璇︾粏錛岀己鐪佷負(fù)0
digest 瀛樺偍瀵嗙爜鐨勫姞瀵嗘柟寮忥紝濡傛灉涓嶆寚瀹氬垯鏄槑鏂囧瓨鍌ㄣ傛寚瀹氫負(fù) java.security.MessageDigest 涔嬬被鐨勭被鍚嶅垯瑕佺湅鏁版嵁搴撻噷琛ㄤ腑鐢ㄦ埛瀵嗙爜鐨勫瓨鏀炬牸寮忋?
driverName 鏁版嵁搴撻┍鍔ㄧ▼搴忕被
roleNameCol 瑙掕壊琛ㄧ殑瀛樻斁瑙掕壊鍚嶇殑瀛楁鍚?
userCredCol 鐢ㄦ埛琛ㄩ噷瀛樻斁瀵嗙爜鐨勫瓧孌靛悕
userNameCol 鐢ㄦ埛琛ㄤ腑瀛樻斁鐢ㄦ埛鍚嶇殑瀛楁鍚?BR>
userRoleTable 瑙掕壊琛ㄧ殑琛ㄥ悕錛堢被浼?etc/group錛?BR>
userTable 鐢ㄦ埛琛ㄧ殑琛ㄥ悕
娉ㄦ剰鐐?
濡傛灉浣犲鐢ㄦ埛琛ㄨ繘琛屼簡(jiǎn)鏂板鎿嶄綔鍜屼慨鏀規(guī)搷浣滐紝閭d箞浼?xì)瀹炴椂浣滅敤浜庢瑕仒q涜鐧婚檰鎿嶄綔鐨勭敤鎴鳳紱
鐢ㄦ埛宸茬粡瀹屾垚鐧婚檰鍚庯紝浣犲瀹冭繘琛岀殑鍒犻櫎淇敼鎿嶄綔錛屽茍涓嶈兘瀹炴椂浣滅敤浜庣敤鎴風(fēng)殑褰撳墠鐘舵侊紝鍙兘鍦ㄦ鐢ㄦ埛涓嬫鐧婚檰鐨勬椂鍊欑敓鏁堬紱錛堝鏋滄槸鍩轟簬琛ㄥ崟璁よ瘉鐨勭敤鎴鳳紝鏄湪浼?xì)璇澗l撴潫鎴栬呬粬娉ㄩ攢鍚庡綋鍓嶈璇佸け鏁堬紱濡傛灉鏄熀紜璁よ瘉鐨勭敤鎴峰垯闇瑕佺瓑鍒板綋鍓嶇獥鍙e叧闂級(jí)
瀵規(guī)暟鎹簱閲岄偅涓や釜琛ㄧ殑澧炲垹鏀圭鐞嗭紝浣犻渶瑕佽嚜琛岀紪鍐欏悎閫傜殑涓氬姟浠g爜錛孴omcat騫舵病鏈夋彁渚涙爣鍑嗙殑瀹炵幇錛岃繖鏄病鏈夋剰涔夌殑銆?
緙栬瘧鑰呮敞錛氳繖閮ㄥ垎鍐呭鏄府鍔﹏ewbie鐞嗚ВRealm鑰岀洿鎺ヤ粠Realm Configuration HOW-TO涓憳璇戠殑錛屾槸鎴戜滑瀹屾暣渚嬪瓙鎵蹇呴』瑕佷簡(jiǎn)瑙e茍姝g‘閰嶇疆鐨勯儴鍒嗭紝涓嶈繃浼間箮娌℃湁鐪嬪埌綾諱技鐨勮瘧鏂囷紝灝卞仛浜?jiǎn)杩欎欢绡忚禈鐨勪簨鎯呫傝繖緋誨垪鏂囩珷瀵圭啛鎵嬪熀鏈病鏈変粈涔堝府鍔╋紝璇瘋璋呫?
瀹夎姝ラ:
1.瀹夎jdk
2.瀹夎tomcat
3.瀹夎mysql
4.瀹夎mysqlcc
5.灝嗛┍鍔ㄥ寘瑙e帇,鎷瘋礉mysql-connector-java-3.0.14-production-bin.jar鍒皌omcat/common/lib涓?BR>鎴栬呬笅杞絤m.mysql-2.0.14-you-must-unjar-me.jar,瑙e帇鍚庢嫹璐濆叾涓殑mm.mysql-2.0.14-bin.jar
Tomcat5.0閰嶇疆 鏈緥浣跨敤瀹夎瀵嗙爜 198277
1.閰嶇疆manager 綆$悊搴旂敤紼嬪簭
鍦╟onf/server.xml 涓?BR>娣誨姞濡備笅
<Service name="Catalina">
...
<Context path="/manager" debug="0" privileged="true"
docBase="/usr/local/kinetic/tomcat5/server/webapps/manager">
</Context>
</Service>
闄愬埗ip璁塊棶閰嶇疆
<Context path="/manager" debug="0" privileged="true"
docBase="/usr/local/kinetic/tomcat5/server/webapps/manager">
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127.0.0.1"/>
</Context>
嫻嬭瘯涓?http://localhost:8080/manager/html
2.閰嶇疆JDBCRealm瀹瑰櫒綆$悊瀹夊叏,浠ysql-4.0鏁版嵁搴撲負(fù)渚?BR>a.鎷瘋礉椹卞姩mm.mysql-2.0.14-bin.jar鍒癱ommon/lib/涓?BR>b.鍦ㄦ暟鎹簱ycg涓緩琛?BR>
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)
);
c.淇敼server.xml濡備笅(榛樿鏁版嵁搴撲負(fù)root,鏃犲瘑鐮?濡傛灉鏈夊艦濡?connectionURL="jdbc:mysql://localhost/authority?
user=dbuser&password=dbpass")
<Realm className="org.apache.catalina.realm.JDBCRealm" debug="99"
driverName="org.gjt.mm.mysql.Driver"
connectionURL="jdbc:mysql://localhost/ycg?user=root"
connectionName="" connectionPassword=""
userTable="users" userNameCol="user_name" userCredCol="user_pass"
userRoleTable="user_roles" roleNameCol="role_name" />
d.鍦ㄦ暟鎹簱涓坊鍔犲叆tomcat鐨勯粯璁ら厤緗暟鎹?
+-----------+-----------+
| user_name | role_name |
+-----------+-----------+
| admin | admin |
| admin | manager |
| both | role1 |
| both | tomcat |
| role1 | role1 |
| tomcat | tomcat |
+-----------+-----------+
+-----------+-----------+
| user_name | user_pass |
+-----------+-----------+
| tomcat | tomcat |
| both | tomcat |
| role1 | tomcat |
| admin | 198277 |
+-----------+-----------+
e.鍚姩mysql,鍚姩tomcat,姝ゅ悗tomcat灝嗕粠鏁版嵁搴撲腑璇葷敤鎴瘋鍒欒璇?榛樿鐨刢onf/tomcat-users.xml澶辨晥
3.DBCP鐨勯厤緗?BR>a.璁劇疆
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
鍙嬌澶辨晥鐨勬暟鎹繛鎺ラ噸鏂板惎鐢?
閰嶅璁劇疆
<parameter>
<name>removeAbandonedTimeout</name>
<value>60</value>
</parameter>
澶辨晥鏃墮棿
濡傛灉瑕佸啓鍏ユ棩蹇?BR>璁劇疆
<parameter>
<name>logAbandoned</name>
<value>true</value>
</parameter>
浠ヤ笂涓変釜榛樿閮芥槸false
b.浠ysql涓轟緥,閰嶇疆鏁版嵁榪炴帴姹?BR>c.閰嶇疆鏂扮殑鐢ㄦ埛涓庢暟鎹簱,蹇呴』璁懼畾瀵嗙爜,絀哄瘑鐮佸皢瀵艱嚧榪炴帴澶辮觸
e.
鎸囧畾root瀵嗙爜:mysqladmin -u root -h localhost password "198277"
(闇淇敼涓婇潰鐨刯dbcrealm璁劇疆connectionURL="jdbc:mysql://localhost/ycg?user=root&password=198277")
鍛戒護(hù)mysql榪涘叆鍖垮悕榪炴帴鍒版湇鍔″櫒
瀵嗙爜璁塊棶
shell> mysql -h host -u user -p
Enter password: ********
//濡傛灉root娌℃湁瀵嗙爜,浠ヤ笅鏄笉鎴愬姛鐨?(璇曡繃浜?
mysql> GRANT ALL PRIVILEGES ON *.* TO javauser@localhost
-> IDENTIFIED BY 'javadude' WITH GRANT OPTION;
mysql> create database javatest;
mysql> use javatest;
mysql> create table testdata (
-> id int not null auto_increment primary key,
-> foo varchar(25),
-> bar int);
鍦╟onf/server.xml涓?lt;host></host>涓坊鍔?BR><Context path="/DBTest" docBase="DBTest"
debug="5" reloadable="true" crossContext="true">
<Logger className="org.apache.catalina.logger.FileLogger"
prefix="localhost_DBTest_log." suffix=".txt"
timestamp="true"/>
<Resource name="jdbc/TestDB"
auth="Container"
type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/TestDB">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<!-- Maximum number of dB connections in pool. Make sure you
configure your mysqld max_connections large enough to handle
all of your db connections. Set to 0 for no limit.
-->
<parameter>
<name>maxActive</name>
<value>100</value>
</parameter>
<!-- Maximum number of idle dB connections to retain in pool.
Set to 0 for no limit.
-->
<parameter>
<name>maxIdle</name>
<value>30</value>
</parameter>
<!-- Maximum time to wait for a dB connection to become available
in ms, in this example 10 seconds. An Exception is thrown if
this timeout is exceeded. Set to -1 to wait indefinitely.
-->
<parameter>
<name>maxWait</name>
<value>10000</value>
</parameter>
<!-- MySQL dB username and password for dB connections -->
<parameter>
<name>username</name>
<value>javauser</value>
</parameter>
<parameter>
<name>password</name>
<value>javadude</value>
</parameter>
<!-- Class name for the old mm.mysql JDBC driver - uncomment this entry and comment next
if you want to use this driver - we recommend using Connector/J though
<parameter>
<name>driverClassName</name>
<value>org.gjt.mm.mysql.Driver</value>
</parameter>
-->
<!-- Class name for the official MySQL Connector/J driver -->
<parameter>
<name>driverClassName</name>
<value>com.mysql.jdbc.Driver</value>
</parameter>
<!-- The JDBC connection url for connecting to your MySQL dB.
The autoReconnect=true argument to the url makes sure that the
mm.mysql JDBC Driver will automatically reconnect if mysqld closed the
connection. mysqld by default closes idle connections after 8 hours.
-->
<parameter>
<name>url</name>
<value>jdbc:mysql://localhost:3306/javatest?autoReconnect=true</value>
</parameter>
<parameter>
<name>removeAbandoned</name>
<value>true</value>
</parameter>
<parameter>
<name>removeAbandonedTimeout</name>
<value>60</value>
</parameter>
<parameter>
<name>logAbandoned</name>
<value>true</value>
</parameter>
</ResourceParams>
</Context>
f.鍦╳eb鏈嶅姟涓皟鐢?閰嶇疆web.xml 濡? <sql:query var="rs" dataSource="jdbc/TestDB"> <html> <h2>Results</h2> </body> h.鏂板緩web搴旂敤 DBTest/ 4.ssl鐨勯厤緗?浠dk1.4.2涓轟緥 杈撳叆<tomcat>鐨勪富瀵嗙爜 <!-- 娣誨姞閰嶇疆瀛楁:keystoreFile="/conf/.keystore" keystorePass="198277" <description>MySQL Test App</description> <security-constraint> g.濡傛灉涓?閰嶇疆鐨刯dbcRealm緇撳悎璧鋒潵榪涜琛ㄥ崟璁よ瘉 鐒跺悗鍦╳eb.xml涓坊鍔?BR><auth-constraint> <login-config> 淇敼鍚庣殑web.xml濡? <description>MySQL Test App</description> <security-constraint> 嫻嬭瘯: 5.涓枃涔辯爜闂: }
<web-app xmlns=" xmlns:xsi=" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
version="2.4">
<description>MySQL Test App</description>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
g.嫻嬭瘯鐢╰est.jsp
<%@ taglib uri="<%@ taglib uri="
select id, foo, bar from testdata
</sql:query>
<head>
<title>DB Test</title>
</head>
<body>
<c:forEach var="row" items="${rs.rows}">
Foo ${row.foo}<br/>
Bar ${row.bar}<br/>
</c:forEach>
</html>
涓嬭澆jakarta-taglibs-standard-1.1.0
copy jstl.jar and standard.jar to your web app's WEB-INF/lib
WEB-INF/
web.xml
lib/
jstl.jar
standard.jar
test.jsp
鎷瘋礉鍒皐ebapps/ 涓?BR>i.鍚姩mysql,tomcat
璁塊棶:
http://localhost:8080/DBTest/test.jsp
鏄劇ず:
Results
Foo hello
Bar 12345
a.榪涘叆%JAVA_HOME%\bin
榪愯鍛戒護(hù):keytool -genkey -alias tomcat -keyalg RSA
浠omcat 瀹夎瀵嗙爜涓?98277,ketool璁劇疆瀵嗙爜涓?98277涓轟緥
杈撳叆keystore瀵嗙爜錛?nbsp; 198277
鎮(zhèn)ㄧ殑鍚嶅瓧涓庡姘忔槸浠涔堬紵
[Unknown]錛?nbsp; ycg
鎮(zhèn)ㄧ殑緇勭粐鍗曚綅鍚嶇О鏄粈涔堬紵
[Unknown]錛?nbsp; nju
鎮(zhèn)ㄧ殑緇勭粐鍚嶇О鏄粈涔堬紵
[Unknown]錛?nbsp; nju
鎮(zhèn)ㄦ墍鍦ㄧ殑鍩庡競(jìng)鎴栧尯鍩熷悕縐版槸浠涔堬紵
[Unknown]錛?nbsp; nanjing
鎮(zhèn)ㄦ墍鍦ㄧ殑宸炴垨鐪佷喚鍚嶇О鏄粈涔堬紵
[Unknown]錛?nbsp; jiangsu
璇ュ崟浣嶇殑涓ゅ瓧姣嶅浗瀹朵唬鐮佹槸浠涔?BR> [Unknown]錛?nbsp; nd
CN=ycg, OU=nju, O=nju, L=nanjing, ST=jiangsu, C=nd 姝g‘鍚楋紵
[鍚錛?nbsp; y
錛堝鏋滃拰 keystore 瀵嗙爜鐩稿悓錛屾寜鍥炶濺錛夛細(xì) 198277
b.鍦ㄤ綘鐨凞:\Documents and Settings\鐨勫綋鍓嶇敤鎴風(fēng)洰褰曚笅鍙互鎵懼埌.keystore鏂囦歡.灝嗗叾鎷瘋礉鍒癱onf/鏂囦歡澶逛笅.
c.鍦╯erver.xml 涓壘鍒?/P>
<Connector port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" debug="0" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" />
-->
鍘繪帀娉ㄩ噴
濡? <!-- Define a SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector port="8443"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" disableUploadTimeout="true"
acceptCount="100" debug="0" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" keystoreFile="/conf/.keystore"
keystorePass="198277"/>
d.嫻嬭瘯涓?
https://localhost:8443
e.鍦ㄨ嚜宸辯殑紼嬪簭涓坊鍔爏sl璁よ瘉鏂瑰紡涓?
鍦╳eb.xml 涓?lt;web-app></web-app>娣誨姞
<security-constraint>
<web-resource-collection>
<web-resource-name>Success</web-resource-name>
<url-pattern>/</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
f.鐢ㄤ笂鎻愪負(fù)渚嬪氨鏄?BR>淇敼web.xml 涓?BR><web-app xmlns=" xmlns:xsi=" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
version="2.4">
<web-resource-collection>
<web-resource-name>Success</web-resource-name>
<url-pattern>/</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
璁塊棶:
https://localhost:8443/DBTest/test.jsp
鍏堝湪user_roles琛ㄤ腑娣誨姞user_name:ycg role_name:web-user
鍦╱sers琛ㄤ腑娣誨姞user_name:ycg user_pass:198277
<role-name>web-user</role-name>
</auth-constraint>
<auth-method>BASIC</auth-method>
<realm-name>My Member Area</realm-name>
</login-config>
<web-app xmlns=" xmlns:xsi=" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
version="2.4">
<web-resource-collection>
<web-resource-name>Success</web-resource-name>
<url-pattern>/</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>web-user</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>My Member Area</realm-name>
</login-config>
<resource-ref>
<description>DB Connection</description>
<res-ref-name>jdbc/TestDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</web-app>
http://localhost:8080/DBTest/test.jsp
灝嗛氳繃ssl榪炴帴,騫惰繘琛岃〃鍗曡璇?鐢ㄦ埛瀵嗙爜鍙湪user_roles,鍜寀sers涓坊鍔?
mysql 榛樿緙栫爜 iso
tomcat request 浼犺緭緙栫爜 iso
濡傛灉瑕佹樉紺轟腑鏂?BR>鍦?.jsp涓坊鍔?BR><head>
<%@ page
language="java"
contentType="text/html; charset=GB18030"
pageEncoding="GB18030"
%>
</head>
濡傛灉鏄暟鎹紶杈撲腑鐨勪貢鐮?濡傜敤servlet浠巑ysql鏁版嵁搴撹鍑虹殑鏁版嵁)
鐢ㄤ互涓嬩袱涓漿鐮佸嚱鏁拌漿鐮?濡傛灉涓嶆竻妤氱敱鍝緙栫爜杞垚鍝緙栫爜,灝卞灝濊瘯.
//杞爜GBK杞琁SO
public String toISO(String input) {
try {
byte[] bytes = input.getBytes("GBK");
return new String(bytes,"ISO8859-1");
}catch(Exception ex) {
}
return input;
//杞爜IS0杞珿BK
public String toGBK(String input) {
try {
byte[] bytes = input.getBytes("ISO8859-1");
return new String(bytes,"GBK");
}catch(Exception ex) {
}
return input;
}
浠ヤ笂閰嶇疆閮芥祴璇曢氳繃.涓昏鍙傝僼omcat5.0鐨勫府鍔╂枃妗?灝嗚繃紼嬪啓鍑烘潵涓庡ぇ瀹跺叡浜?濡傛灉鍙戠幇鍏朵腑閿欒,璇鋒寚鍑?
嬈㈣繋緇欐垜鏉ヤ俊ycg01@software.nju.edu.cn鍏卞悓鎺㈣.
]]>
July 19th, 2004 at 8:59 am
hi,
I cannot get tagish working! I don鈥檛 know what I鈥檓 doing wrong. Someone can help me please? I鈥檝e followed the list above step by step
System: Struts and tomcat on W2000 professional (but also tried same config on W2000 server and it doesn鈥檛 work either)
Thanks
stack trace:
19-Jul-2004 17:17:12 org.apache.catalina.realm.JAASRealm authenticate
WARNING: Login exception authenticating username Administrator
javax.security.auth.login.LoginException: Error: javax.security.auth.callback.TextInputCallback@da3772 not available to garner authentication information from the user
at com.tagish.auth.win32.NTSystemLogin.login(NTSystemLogin.java:128)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:675)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:129)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:610)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokeModule(LoginContext.java:607)
at javax.security.auth.login.LoginContext.login(LoginContext.java:534)
at org.apache.catalina.realm.JAASRealm.authenticate(JAASRealm.java:281)
at org.apache.catalina.authenticator.BasicAuthenticator.authenticate(BasicAuthenticator.java:129)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:504)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:137)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:102)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.core.StandardValveContext.invokeNext(StandardValveContext.java:104)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:520)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:929)
at org.apache.coyote.tomcat5.CoyoteAdapter.service(CoyoteAdapter.java:160)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:793)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:702)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:571)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:644)
at java.lang.Thread.run(Thread.java:534)
19-Jul-2004 17:20:17 org.apache.coyote.http11.Http11Protocol pause
INFO: Pausing Coyote HTTP/1.1 on http-8080
July 19th, 2004 at 10:53 am
I saw this problem; it was caused by a mistake in the config files. Does Tomcat throw an earlier exception that complains about parsing the tagish login config file? If the parse fails, the callback handler will not be initialized.
August 31st, 2004 at 7:20 am
I am also facing the same issue.
WARNING: Login exception authenticating username Administrator
javax.security.auth.login.LoginException: Error: javax.security.auth.callback.TextInputCallback@da3772 not available to garner authentication information from the user
at com.tagish.auth.win32.NTSystemLogin.login(NTSystemLogin.java:128)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
Were you able to resolve the above? Please guide.
September 6th, 2004 at 7:12 pm
I鈥檝e got the Tagish example working fine where I can use the batch login.bat to test a
login/authentication. I can also integrate it with JAAS security and have it authenticate me
against my network in a console application. However, as soon as I try to tie it into
Tomcat, I get stuck. (I鈥檓 trying an web-app example based on BASIC authentication)
I think my files are in the correct places (I get different errors when they鈥檙e missing) and
I can make my tomcat example web-app work using a different authentication method (the
tomcat manager鈥檚 memory-realm user authentication works fine) and I鈥檝e even done the JAAS
example to replace the security realm of the tomcat manager app with JAAS.
The problem I鈥檓 getting now is that I get a serious bomb that kills tomcat as soon as I try
to access the protected directory:
An unexpected exception has been detected in native code outside the VM.
Unexpected Signal : EXCEPTION_ACCESS_VIOLATION (0xc0000005) occurred at PC=0x7BF6256
Function=[Unknown.]
Library=C:\Projects\workspace3\example.Tomcat_NT\lib\NTSystem.dll
NOTE: We are unable to locate the function name symbol for the error
just occurred. Please refer to release documentation for possible
reason and solutions.
Current Java thread:
at com.tagish.auth.win32.NTSystem.logon(Native Method)
- locked <0x10024d98> (a com.tagish.auth.win32.NTSystem)
at com.tagish.auth.win32.NTSystemLogin.login(NTSystemLogin.java:134)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...
I鈥檓 really rather at a loss as to why this is happening. It 鈥榝eels鈥?like there is something
wrong with the tagish module, but if others got it working I really don鈥檛 know. Did you have
to extend it at all? Or wrap it up in an implemented JAAS interface? (but tagish has done
that already haven鈥檛 they?)
Config:
Tomcat 5.0.27/28
Eclipse 3.0
Sysdeo eclipse/tomcat plugin
J2SDK 1.4.2_05
Tagish 1.0.3
I also tried plugging the tagish module straight into a clean tomcat (without using any of
my code) to replace the security realm of the manager web-app. The error is slightly
different here:
7-Sep-2004 12:46:17 AM org.apache.catalina.realm.JAASRealm authenticate
WARNING: Login exception authenticating username louis
javax.security.auth.login.LoginException: Error: javax.security.auth.callback.Te
xtInputCallback@354749 not available to garner authentication information from t
he user
at com.tagish.auth.win32.NTSystemLogin.login(NTSystemLogin.java:128)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAcces
sorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at javax.security.auth.login.LoginContext.invoke(LoginContext.java:675)
at javax.security.auth.login.LoginContext.access$000(LoginContext.java:1
29)
at javax.security.auth.login.LoginContext$4.run(LoginContext.java:610)
at java.security.AccessController.doPrivileged(Native Method)
at javax.security.auth.login.LoginContext.invokeModule(LoginContext.java
:607)
at javax.security.auth.login.LoginContext.login(LoginContext.java:534)
at org.apache.catalina.realm.JAASRealm.authenticate(JAASRealm.java:316)
at org.apache.catalina.authenticator.BasicAuthenticator.authenticate(Bas
icAuthenticator.java:129)
Does anyone have a clean working example in tomcat they鈥檇 be willing to zipup and post?
My proper email prefix is: louis_a_j
Suffix: telus.net
September 6th, 2004 at 7:33 pm
P.S. As per the following post:
Tagish and JBOSS
I鈥檝e tried substituting a default domain into the tagish.login (my workgroup) but that just brought me back to the previous exception which kills tomcat.
Note: I鈥檝e tried this on both an NT4 domain and on a windows 2000 workgroup with the same results in both.
September 7th, 2004 at 8:15 am
Sorry, the link I included broke:
http://www.jboss.org/index.html?module=bb&op=viewtopic&p=3837312#3837312
November 3rd, 2004 at 3:06 pm
I have the same problem as the #4 comment on this page submited by Louis Johannson. I can authenticate using a console app, but when I try and follow the instructions to authenticate agains Tomcat, my jvm blows up with the same error. It seems that there is a problem with NTSystem.dll. If it is anywhere on my path, my jvm will blow up when I try and use tomcat. If I delete it, then I get errors about it not being found. Has anyone found a solution to this? Any help would be appreciated.
November 4th, 2004 at 7:19 am
I have found the solution to the problem described by post #4. The NTSystem.dll doesn鈥檛 appear to like a null username. When trying to get this to work with Tomcat, if you use Form based authentication, you can avoid sending a null username, and everything works great.
November 19th, 2004 at 5:50 am
Hi
I try to use samba ntlm filter first and I鈥檝e got some problem.
If I try to open some web page from my web app the internet browser logon dialog always shows.
How should I configure my Windows XP workstation to eliminate this dialog.
January 14th, 2005 at 7:25 am
Hi,
I have a question about automatic authentication.
I have a windows 2003 server with active directory. Tomcats and JAAS libraries are correctly installed and configured. When I try to access to the web site I protected by the realm, a login/password is requiered, and I can access to the web site after authentication.
My question is: is it possible not to have the login/password dialog box if I access to Web site with a computer that belongs to the domain I create and if i鈥檓 logged with the correct login/password to have access.
I would automatically enter to the web site as I鈥檓 already authenticated in the Windows session. Is that possible?
The case is the same if I try locally, on the server. I鈥檓 logged with an account who belongs to the domain, and this account belongs to the group I defined in the active directory and in the web.xml file of the web site. But the system ask me the login/password and don鈥檛 acces directly to the web site.
Thanks
January 18th, 2005 at 2:23 pm
I鈥檓 starting down this path also and having some troubles. One thing I noticed there is a typo in the instructions. I think you should update the java.security file like so:
login.config.url.1=file:${java.home}/jre/lib/security/tagish.login
Plus I added the following to Tagish鈥檚 login.bat:
-Djava.security.auth.login.config=C:\j2sdk1.4.2_06\jre\lib\security\tagish.login
Next step鈥uthenticating in Tomcat鈥?/P>
- Mel
March 4th, 2005 at 5:09 pm
I modified the NTSystemLogin source in the Tagish JAAS module to throw a FailedLoginException for a null username or password,a nd not normal BASIC auth works. I presume the null was blowing up in the Tagish NTSYstem.dll or the call it makes to the Win32 API. I shared my update with the Tagish JAAS author, so I鈥檒l see what he thinks.
April 1st, 2005 at 9:35 am
Hi, I am trying to use tagish with tomcat 5.0.27 and exactly getting all the same problems mentioned by others. Is there any clear way to use tagish??
Please advice.
thanks
aks
May 4th, 2005 at 2:05 pm
does this also work on websphere server?
July 27th, 2005 at 1:47 pm
Aaron Hawkins, thanks a million. My tomcat 5.0.28 was also crashing with the Basic authentication and i noticed the username null entries in the stdout.log and wondered if that was causing it, but never made an attempt to resolve it using form base authentication until reading your message. seemed very unusual that tomcat鈥檚 basic authentication was returning nulls even when the correct username and password were entered on the first try. go figure. Trinition, i haven鈥檛 seen an update with your recommended fix, so i might just hack the java code myself. thanks.
August 4th, 2005 at 9:09 am
The TextInputCallback is issued when you do not have a defaultdomain=鈥漼ourdomain鈥?in your tagish.login file, there is no way for a TextInputCallback to work through the servlet, the source should be changed so that it could parse the domain from the username callback if no default domain is specified, however some people may want to have a text callback for applications instead of servlets.
The Null username does indeed crash the NTSystem.dll, simply change the login attempt:
succeeded = false
ntSystem.logon(username, password, domain); // may throw
succeeded = true;
to:
succeeded = false;
if (username == null || password == null) {
throw new LoginException("Error: " + username == null ? "username" : "password" + ", null, not allowed");
}
ntSystem.logon(username, password, domain); // may throw
succeeded = true;
and recompile the tagish src to a jar file, deploying it in the Tomcat/bin directory like explained above and everything will work correctly even with Basic auth.
This is great and all, but I don鈥檛 see how tagish is using NTLM, unless you mean it is talking to my domain to get my roles using kerberos or NTLM, it does not however provide the transparent login that jCIFS does. I looked throught the tagish source and don鈥檛 see anything in there either, did I misread your article or did I overlook something?