jboss and jaas--BBS卓大苹果园站--北京卓大经济管理学院

来源:百度文库 编辑:神马文学网 时间:2024/06/30 19:46:10

jboss and jaas

作者: hofman  军衔: 上尉  发表时间: 2004-06-20 22:47:17

http://members.capmac.org/~orb/blog.cgi/tech/java/jaas_jboss.html

I have a good bit of experience in authentication systems. Back when I wrote LDAP servers for living, I spent a lot of time working on LDAP authentication modules for apache, PAM and other systems. As a Java coder, I've had to code several authentication and single sign on systems over the years, but I've only recently had the chance to try the Sun blessed JAAS in a project. I needed to port a web application from a proprietary web container to JBoss, and authentication was a big part of that.

It turns out to be amazingly simple to use JAAS in JBoss. That's no surprise as everything is amazingly simple in JBoss if you are lucky enough to be able to find documentation for your task. In JBoss, adding an authentication system is as simple as providing a LoginModule implementation. You can tell JBoss about a new LoginModule by simply adding a directive to conf/login-config.xml. The format is straightforward.

JBoss provides a number of ready to use modules including authentication against LDAP servers, relational databases and properties files. If none of the provided modules work, you can always write your own module from scratch or extend one of the abstract modules. In this particular project, user names and passwords are stored in a relational database. However, the passwords are base64 encoded, (why? I don't know) and so I couldn't use the DatabaseServerLoginModule directly. DatabaseServerLoginModule has a convenient hook for this, so I simply need to provide a subclass that looks something like the following.

public class MyLoginModule
extends DatabaseServerLoginModule
{
protected String convertRawPassword(String password)
{
try {
return new String((new sun.misc.BASE64Decoder()).decodeBuffer(password));
} catch (IOException e) {
return password;
}
}
}

My login-config.xml contains the following.





and users.roleId=userroles.roleId





After that, configuring the application to use the new authentication module was as simple as adding "java:/jaas/MyAuth" to my jboss-web.xml file and applying the standard security-constraint and security-role settings in web.xml.

A few notes to make like with JAAS in JBoss a bit simpler:

If your LoginModule has problems (bad configuration, for example), JBoss isn't very good at warning you. You'll have to turn logging up to make sure you see the errors. JBoss will silently switch to the "other" module (defined in login-config.xml) without telling you.
The documentation suggest that you can place an applications specific login-config.xml in your ear file, but I wasn't able to make it work. However, even though you provide the LoginModule definition in the global login-config.xml, you can still provide the implementation classes in your ear/war file. (you don't have to deploy the LoginModule separately)
The JMX console provides information about your JAAS modules under "service=XMLLoginConfig". From the console, invoke displayAppConfig with the name of your your application policy. If your module doesn't come up, you know it's not being seen.
Your JAAS modules will also show up under the java:/jaas namespace. (Use the list method on service=JNDIView to view this) However, you can't interact with them from the JNDIView.

»回复此文章   Admin

Re:jboss

作者: hofman  军衔: 上尉  发表时间: 2004-06-20 23:09:02

http://www.javaworld.com/javaforums/showflat.php?Cat=2&Board=JavaSecurity&Number=2500&page=2&view=collapsed&sb=5&o=&fpart=1

make JDBC setting as per ur need

JAAS Configuration

1. Database
Create following table:

a. Principals table consists of usernames (PrincipalID) and their passwords.

CREATE TABLE Principals (PrincipalID VARCHAR (64) PRIMARY KEY,
Password VARCHAR (64))

Insert data
INSERT INTO Principals VALUES ('java', 'echoman')
INSERT INTO Principals VALUES ('duke', 'javaman')

b. Roles table consists of usernames (PrincipalID) and their Role and the
RoleGroup they belong.
CREATE TABLE Roles (PrincipalID VARCHAR (64), Role
VARCHAR (64), RoleGroup VARCHAR (64))

Insert data
INSERT INTO Roles VALUES ('java', 'Echo', 'Roles')
INSERT INTO Roles VALUES ('java', 'caller_java', 'CallerPrincipal')
INSERT INTO Roles VALUES ('duke', 'Java', 'Roles')
INSERT INTO Roles VALUES ('duke', 'Coder', 'Roles')
INSERT INTO Roles VALUES ('duke', 'caller_duke', 'CallerPrincipal')
INSERT INTO Roles VALUES ('duke', 'Echo', 'Roles')


2. login-config.xml

This file is located in jboss-3.2.1\server\default\conf

a. add the following lines

jboss.security.ClientLoginModule" flag="required"]

jboss.security.auth.spi.DatabaseServerLoginModule"
flag="required"]
jboss.jca:service=LocalTxCM,name=SybaseDB

java:/SybaseDB

Select Password from Principals where PrincipalID =?

Select Role 'Roles', RoleGroup 'RoleGroups' from Roles where PrincipalID =?





3. jboss-web.xml
Create a file jboss-web.xml and place the following code


<jboss-web>
java:/jaas/example2
jboss-web>

example2 is the name of the security domain which we specified in application policy of login-config.xml
Copy this file in your applications WEB-INF folder

4. auth.conf
Create a file auth.conf and place it in jboss-3.2.1\client.

client-login
{
org.jboss.security.ClientLoginModule required;
};

example2
{
org.jboss.security.ClientLoginModule required;
org.jboss.security.auth.spi.DatabaseServerLoginModule required;
};

5. auth.conf
Create another auth.conf and place it in jboss-3.2.1\server\default\conf

// The JBoss server side JAAS login config file for the examples

client-login
{
org.jboss.security.ClientLoginModule required;
};
example2
{
org.jboss.security.ClientLoginModule required;
org.jboss.security.auth.spi.DatabaseServerLoginModule
required
dsJndiName="java:/SybaseDB"
principalsQuery="Select Password from Principals where PrincipalID =?"
rolesQuery="Select Role 'Roles', RoleGroup 'RoleGroups' from Roles where PrincipalID =?"
;
};



5. jndi

Path jboss-3.2.1\server\default\conf

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
# Do NOT uncomment this line as it causes in VM calls to go over
# RMI!
java.naming.provider.url=localhost:1099
#localhost



6. web.xml
Place the following code in your web.xml.(Change it according to your application requirements).



action
Declarative security tests
*.do
HEAD
GET
POST
PUT
DELETE

// the role which can access these resources

Echo



no description
NONE


//the login page in case of Basic authentication

//the login page in case of form based authentication

FORM

/logon.do //path to login page
/logoff.do //path in case login fails



A user allowed to invoke echo methods
Echo










7. login.jsp

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ page language="java" %>




<%
response.setHeader("Cache-Control","no-cache"); // HTTP 1.1
response.setHeader("Pragma","no-cache"); // HTTP 1.0
response.setDateHeader ("Expires", -1); // Prevents caching at the proxy server
%>




"1" bgcolor="white"]
"0" cellpadding="5"]







name="j_password" maxlength=20 >








8. Your action class should contain the following code
a. Pacakages to be imported

import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.jboss.security.SimplePrincipal;
import org.jboss.security.auth.callback.SecurityAssociationHandler;

try
{
SecurityAssociationHandler handler = new
SecurityAssociationHandler();
user = new SimplePrincipal(username);
handler.setSecurityInfo(user, password.toCharArray());
LoginContext loginContext = new LoginContext("example2",
(CallbackHandler)handler);
loginContext.login();
Subject subject = loginContext.getSubject();
Set principals = subject.getPrincipals();
principals.add(user);
}catch(LoginException e)
{ errors.add("loginerror", new ActionError("Wrong Username or Password")); saveErrors(request, errors);
}
//other login related code


»回复此文章   Admin

Re:Re:jboss

作者: hofman  军衔: 上尉  发表时间: 2004-06-20 23:48:27

http://www.jboss.org/index.html?module=bb&op=viewtopic&t=49471


package org.jboss.security.auth.spi;

import java.security.acl.Group;
import java.util.HashMap;
import java.util.Map;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;
import javax.security.auth.login.FailedLoginException;
import org.jboss.security.SimpleGroup;
import org.jboss.security.SimplePrincipal;

public class DatabaseServerGroupLoginModule extends DatabaseServerLoginModule {

private String dsJndiName = "java:/DefaultDS";
private String userQuery = "SELECT PASSWORD FROM SYS_USER WHERE ID=?";
private String roleQuery = "SELECT A.ID,A.ROLE_GROUP FROM SYS_ROLE A,SYS_USER_SYS_ROLE_LINK B WHERE A.ID=B.SYS_ROLE_ID AND B.SYS_USER_ID=?";
private String groupQuery = "SELECT A.ID,A.ROLE_GROUP FROM SYS_ROLE A,SYS_USER_SYS_GROUP_LINK B,SYS_GROUP_SYS_ROLE_LINK C WHERE A.ID=C.SYS_ROLE_ID AND B.SYS_GROUP_ID=C.SYS_GROUP_ID AND B.SYS_USER_ID=?";

/**
* Initialize this LoginModule.
*/
public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {
super.initialize(subject, callbackHandler, sharedState, options);

Object tmp = options.get("dsJndiName");
if (tmp != null) {
dsJndiName = tmp.toString();
}

tmp = options.get("userQuery");
if (tmp != null) {
userQuery = tmp.toString();
}

tmp = options.get("roleQuery");
if (tmp != null) {
roleQuery = tmp.toString();
}

tmp = options.get("groupQuery");
if (tmp != null) {
groupQuery = tmp.toString();
}

log.trace("DatabaseServerLoginModule, dsJndiName=" + dsJndiName);
log.trace("userQuery=" + userQuery);
log.trace("roleQuery=" + roleQuery);
log.trace("groupQuery=" + roleQuery);
}

/**
* Get the expected password for the current username available via the
* getUsername() method. This is called from within the login() method after
* the CallbackHandler has returned the username and candidate password.
*
* @return the valid password String
*/
protected String getUsersPassword() throws LoginException {
String username = getUsername();
String password = null;
Connection conn = null;
PreparedStatement ps = null;

try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(dsJndiName);
conn = ds.getConnection();
ps = conn.prepareStatement(userQuery);
ps.setString(1, username);
ResultSet rs = ps.executeQuery();
if (rs.next() == false) {
throw new FailedLoginException("No matching username found in SYS_USER");
}
password = rs.getString(1);
password = convertRawPassword(password);
rs.close();
} catch (NamingException e) {
throw new LoginException(e.toString(true));
} catch (SQLException e) {
log.error("Query failed", e);
throw new LoginException(e.toString());
} finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
}
}
}
return password;
}

/**
* Overriden by subclasses to return the Groups that correspond to the to the
* role sets assigned to the user. Subclasses should create at least a Group
* named "Roles" that contains the roles assigned to the user. A second
* common group is "CallerPrincipal" that provides the application identity
* of the user rather than the security domain identity.
*
* @return Group[] containing the sets of roles
*/
protected Group[] getRoleSets() throws LoginException {
String username = getUsername();
Connection conn = null;
HashMap setsMap = new HashMap();
PreparedStatement ps = null;
boolean empty = false;

try {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup(dsJndiName);
conn = ds.getConnection();
ps = conn.prepareStatement(roleQuery);
ps.setString(1, username);
ResultSet rs = ps.executeQuery();
if (rs.next() != false) {
do {
String name = rs.getString(1);
String groupName = rs.getString(2);
if (groupName == null || groupName.length() == 0)
groupName = "Roles";
Group group = (Group) setsMap.get(groupName);
if (group == null) {
group = new SimpleGroup(groupName);
setsMap.put(groupName, group);
}
group.addMember(new SimplePrincipal(name));
} while (rs.next());
} else {
empty = true;
}
rs.close();
ps.close();

ps = conn.prepareStatement(groupQuery);
ps.setString(1, username);
rs = ps.executeQuery();
if (rs.next() != false) {
do {
String name = rs.getString(1);
String groupName = rs.getString(2);
if (groupName == null || groupName.length() == 0)
groupName = "Roles";
Group group = (Group) setsMap.get(groupName);
if (group == null) {
group = new SimpleGroup(groupName);
setsMap.put(groupName, group);
}
group.addMember(new SimplePrincipal(name));
} while (rs.next());
} else if (empty) {
if (getUnauthenticatedIdentity() == null) {
throw new FailedLoginException("No matching username found in Groups");
}
Group[] roleSets = { new SimpleGroup("Roles")};
return roleSets;
}
rs.close();
} catch (NamingException ex) {
throw new LoginException(ex.toString(true));
} catch (SQLException ex) {
super.log.error("SQL failure", ex);
throw new LoginException(ex.toString());
} finally {
if (ps != null) {
try {
ps.close();
} catch (SQLException e) {
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception ex) {
}
}
}
Group[] roleSets = new Group[setsMap.size()];
setsMap.values().toArray(roleSets);
return roleSets;
}
}

»回复此文章   Admin

Re:Re:Re:jboss

作者: hofman  军衔: 上尉  发表时间: 2004-06-20 23:53:17

The code listed below gives you user, role and group with many to many between all 3 entities.

The tables are:

CREATE TABLE SYS_ROLE (
NAME varchar(30),
ROLE_GROUP` varchar(30),
NOTE varchar(255),
PRIMARY KEY (NAME)
)

CREATE TABLE SYS_ROLE_SYS_GROUP_LINK(
SYS_GROUP_NAME varchar(30),
SYS_ROLE_NAME varchar(30),
PRIMARY KEY (SYS_GROUP_NAME,SYS_ROLE_NAME)
)

CREATE TABLE SYS_USER (
USERNAME varchar(30),
PASSWORD varchar(30),
CONTACT_DETAIL_ID int(11),
ACTIVE tinyint(1) NOT NULL,
NOTE varchar(255),
PRIMARY KEY (USERNAME)
)

CREATE TABLE SYS_USER_SYS_GROUP_LINK (
SYS_GROUP_NAME` varchar(30),
SYS_USER_USERNAME` varchar(30),
PRIMARY KEY (SYS_GROUP_NAME,SYS_USER_USERNAME)
)

CREATE TABLE SYS_USER_SYS_ROLE_LINK (
SYS_ROLE_NAME varchar(30),
SYS_USER_USERNAME varchar(30),
PRIMARY KEY (SYS_ROLE_NAME,SYS_USER_USERNAME)
)

CREATE TABLE SYS_GROUP (
`NAME` varchar(30),
`NOTE` varchar(255),
PRIMARY KEY (NAME)
)


»回复此文章   Admin

Re:Re:Re:Re:jboss

作者: hofman  军衔: 上尉  发表时间: 2004-06-20 23:54:43

protected String getUsername()
{
String username = null;
if( getIdentity() != null )
username = getIdentity().getName();
return username;
}

If I'm right this method uses an identity created in login() method.

My question is:
I need to write getProduct() method but in this case
I need to overwrite login() method in order to create an identity
with name and product inside of it ?
Login() method retrieves username and password, after
Callbackhandler.handle() method, calling getUsernameAndPassword()
method: must I change getUsernameAndPassword() method too ?

»回复此文章   Admin

Re:Re:Re:Re:Re:jboss

作者: hofman  军衔: 上尉  发表时间: 2004-06-21 00:12:43

/*

2 * JBoss, the OpenSource WebOS

3 *

4 * Distributable under LGPL license.

5 * See terms of license at gnu.org.

6 */

7 package org.jboss.security.auth.spi;

8

9 import java.security.acl.Group;

10 import java.util.HashMap;

11 import java.util.Map;

12 import java.sql.Connection;

13 import java.sql.PreparedStatement;

14 import java.sql.ResultSet;

15 import java.sql.SQLException;

16 import javax.naming.InitialContext;

17 import javax.naming.NamingException;

18 import javax.sql.DataSource;

19 import javax.security.auth.Subject;

20 import javax.security.auth.callback.CallbackHandler;

21 import javax.security.auth.login.LoginException;

22 import javax.security.auth.login.FailedLoginException;

23

24 import org.jboss.security.SimpleGroup;

25 import org.jboss.security.SimplePrincipal;

26 import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

27

28 /**

29 * A JDBC based login module that supports authentication and role mapping.

30 * It is based on two logical tables:

31 *


    32 *
  • Principals(PrincipalID text, Password text)

    33 *
  • Roles(PrincipalID text, Role text, RoleGroup text)

    34 *


35 *



36 * LoginModule options:

37 *



    38 *
  • dsJndiName: The name of the DataSource of the database containing the Principals, Roles tables

    39 *
  • principalsQuery: The prepared statement query, equivalent to:

    40 *


    41 * "select Password from Principals where PrincipalID=?"

    42 *


    43 *
  • rolesQuery: The prepared statement query, equivalent to:

    44 *


    45 * "select Role, RoleGroup from Roles where PrincipalID=?"

    46 *


    47 *


48 *

49 * @author
50 * @author Scott.Stark@jboss.org

51 * @version $Revision: 1.6 $

52 */

53 public class DatabaseServerLoginModule extends UsernamePasswordLoginModule

54 {

55 private String dsJndiName;

56 private String principalsQuery = "select Password from Principals where PrincipalID=?";

57 private String rolesQuery = "select Role, RoleGroup from Roles where PrincipalID=?";

58

59 /**

60 * Initialize this LoginModule.

61 */

62 6 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)

63 {

64 6 super.initialize(subject, callbackHandler, sharedState, options);

65 6 dsJndiName = (String) options.get("dsJndiName");

66 6 if( dsJndiName == null )

67 0 dsJndiName = "java:/DefaultDS";

68 6 Object tmp = options.get("principalsQuery");

69 6 if( tmp != null )

70 6 principalsQuery = tmp.toString();

71 6 tmp = options.get("rolesQuery");

72 6 if( tmp != null )

73 6 rolesQuery = tmp.toString();

74 6 log.trace("DatabaseServerLoginModule, dsJndiName="+dsJndiName);

75 6 log.trace("principalsQuery="+principalsQuery);

76 6 log.trace("rolesQuery="+rolesQuery);

77 }

78

79 /** Get the expected password for the current username available via

80 * the getUsername() method. This is called from within the login()

81 * method after the CallbackHandler has returned the username and

82 * candidate password.

83 * @return the valid password String

84 */

85 4 protected String getUsersPassword() throws LoginException

86 {

87 4 String username = getUsername();

88 4 String password = null;

89 4 Connection conn = null;

90 4 PreparedStatement ps = null;

91

92 4 try

93 {

94 4 InitialContext ctx = new InitialContext();

95 4 DataSource ds = (DataSource) ctx.lookup(dsJndiName);

96 4 conn = ds.getConnection();

97 // Get the password

98 4 ps = conn.prepareStatement(principalsQuery);

99 4 ps.setString(1, username);

100 4 ResultSet rs = ps.executeQuery();

101 4 if( rs.next() == false )

102 0 throw new FailedLoginException("No matching username found in Principals");

103

104 4 password = rs.getString(1);

105 4 password = convertRawPassword(password);

106 4 rs.close();

107 }

108 catch(NamingException ex)

109 {

110 0 throw new LoginException(ex.toString(true));

111 }

112 catch(SQLException ex)

113 {

114 0 log.error("Query failed", ex);

115 0 throw new LoginException(ex.toString());

116 }

117 finally

118 {

119 4 if( ps != null )

120 {

121 4 try

122 {

123 4 ps.close();

124 }

125 catch(SQLException e)

126 {}

127 }

128 4 if( conn != null )

129 {

130 4 try

131 {

132 4 conn.close();

133 }

134 catch (SQLException ex)

135 {}

136 }

137 }

138 4 return password;

139 }

140

141 /** Overriden by subclasses to return the Groups that correspond to the

142 to the role sets assigned to the user. Subclasses should create at

143 least a Group named "Roles" that contains the roles assigned to the user.

144 A second common group is "CallerPrincipal" that provides the application

145 identity of the user rather than the security domain identity.

146 @return Group[] containing the sets of roles

147 */

148 4 protected Group[] getRoleSets() throws LoginException

149 {

150 4 String username = getUsername();

151 4 Connection conn = null;

152 4 HashMap setsMap = new HashMap();

153 4 PreparedStatement ps = null;

154

155 4 try

156 {

157 4 InitialContext ctx = new InitialContext();

158 4 DataSource ds = (DataSource) ctx.lookup(dsJndiName);

159 4 conn = ds.getConnection();

160 // Get the users role names

161 4 ps = conn.prepareStatement(rolesQuery);

162 4 ps.setString(1, username);

163 4 ResultSet rs = ps.executeQuery();

164 4 if( rs.next() == false )

165 {

166 0 if( getUnauthenticatedIdentity() == null )

167 0 throw new FailedLoginException("No matching username found in Roles");

168 /* We are running with an unauthenticatedIdentity so create an

169 empty Roles set and return.

170 */

171 0 Group[] roleSets = { new SimpleGroup("Roles") };

172 0 return roleSets;

173 }

174

175 4 do

176 {

177 10 String name = rs.getString(1);

178 10 String groupName = rs.getString(2);

179 10 if( groupName == null || groupName.length() == 0 )

180 0 groupName = "Roles";

181 10 Group group = (Group) setsMap.get(groupName);

182 10 if( group == null )

183 {

184 6 group = new SimpleGroup(groupName);

185 6 setsMap.put(groupName, group);

186 }

187 10 group.addMember(new SimplePrincipal(name));

188 10 } while( rs.next() );

189 4 rs.close();

190 }

191 catch(NamingException ex)

192 {

193 0 throw new LoginException(ex.toString(true));

194 }

195 catch(SQLException ex)

196 {

197 0 super.log.error("SQL failure", ex);

198 0 throw new LoginException(ex.toString());

199 }

200 finally

201 {

202 4 if( ps != null )

203 {

204 4 try

205 {

206 4 ps.close();

207 }

208 catch(SQLException e)

209 {}

210 }

211 4 if( conn != null )

212 {

213 4 try

214 {

215 4 conn.close();

216 }

217 catch (Exception ex)

218 {}

219 }

220 }

221

222 4 Group[] roleSets = new Group[setsMap.size()];

223 4 setsMap.values().toArray(roleSets);

224 4 return roleSets;

225 }

226

227 /** A hook to allow subclasses to convert a password from the database

228 into a plain text string or whatever form is used for matching against

229 the user input. It is called from within the getUsersPassword() method.

230 @param rawPassword, the password as obtained from the database

231 @return the argument rawPassword

232 */

233 4 protected String convertRawPassword(String rawPassword)

234 {

235 4 return rawPassword;

236 }

237 }


»回复此文章   Admin

Re:Re:Re:Re:Re:Re:jboss

作者: hofman  军衔: 上尉  发表时间: 2004-06-21 00:15:04

package org.jboss.security.auth.spi;

8

9 import java.io.IOException;

10 import java.io.UnsupportedEncodingException;

11 import java.util.Map;

12 import java.security.Principal;

13 import java.security.MessageDigest;

14 import java.security.NoSuchAlgorithmException;

15 import java.security.acl.Group;

16 import javax.security.auth.Subject;

17 import javax.security.auth.callback.Callback;

18 import javax.security.auth.callback.CallbackHandler;

19 import javax.security.auth.callback.NameCallback;

20 import javax.security.auth.callback.PasswordCallback;

21 import javax.security.auth.callback.UnsupportedCallbackException;

22 import javax.security.auth.login.LoginException;

23 import javax.security.auth.login.FailedLoginException;

24 import javax.security.auth.spi.LoginModule;

25

26 import org.jboss.security.SimpleGroup;

27 import org.jboss.security.SimplePrincipal;

28 import org.jboss.security.Util;

29 import org.jboss.security.auth.spi.AbstractServerLoginModule;

30

31

32 /** An abstract subclass of AbstractServerLoginModule that imposes

33 * an identity == String username, credentials == String password view on

34 * the login process.

35 *



36 * Subclasses override the getUsersPassword()

37 * and getRoleSets() methods to return the expected password and roles

38 * for the user.

39 *

40 * @see #getUsername()

41 * @see #getUsersPassword()

42 * @see #getRoleSets()

43

44 @author Scott.Stark@jboss.org

45 @version $Revision: 1.11.2.1 $

46 */

47 public abstract class UsernamePasswordLoginModule extends AbstractServerLoginModule

48 {

49 /** The login identity */

50 private Principal identity;

51 /** The proof of login identity */

52 private char[] credential;

53 /** the principal to use when a null username and password are seen */

54 private Principal unauthenticatedIdentity;

55 /** the message digest algorithm used to hash passwords. If null then

56 plain passwords will be used. */

57 private String hashAlgorithm = null;

58 /** the name of the charset/encoding to use when converting the password

59 String to a byte array. Default is the platform's default encoding.

60 */

61 private String hashCharset = null;

62 /** the string encoding format to use. Defaults to base64. */

63 private String hashEncoding = null;

64

65 /** Override the superclass method to look for a unauthenticatedIdentity

66 property. This method first invokes the super version.

67 @param options,

68 @option unauthenticatedIdentity: the name of the principal to asssign

69 and authenticate when a null username and password are seen.

70 */

71 51 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)

72 {

73 51 super.initialize(subject, callbackHandler, sharedState, options);

74 // Check for unauthenticatedIdentity option.

75 51 String name = (String) options.get("unauthenticatedIdentity");

76 51 if( name != null )

77 {

78 15 unauthenticatedIdentity = new SimplePrincipal(name);

79 15 super.log.trace("Saw unauthenticatedIdentity="+name);

80 }

81

82 // Check to see if password hashing has been enabled.

83 // If an algorithm is set, check for a format and charset.

84 51 hashAlgorithm = (String) options.get("hashAlgorithm");

85 51 if( hashAlgorithm != null )

86 {

87 4 hashEncoding = (String) options.get("hashEncoding");

88 4 if( hashEncoding == null )

89 0 hashEncoding = Util.BASE64_ENCODING;

90 4 hashCharset = (String) options.get("hashCharset");

91 4 if( log.isTraceEnabled() )

92 {

93 4 log.trace("Passworg hashing activated: algorithm = " + hashAlgorithm +

94 ", encoding = " + hashEncoding+ (hashCharset == null ? "" : "charset = " + hashCharset));

95 }

96 }

97 }

98

99 /** Perform the authentication of the username and password.

100 */

101 49 public boolean login() throws LoginException

102 {

103 // See if shared credentials exist

104 49 if( super.login() == true )

105 {

106 // Setup our view of the user

107 1 Object username = sharedState.get("javax.security.auth.login.name");

108 1 if( username instanceof Principal )

109 0 identity = (Principal) username;

110 else

111 {

112 1 String name = username.toString();

113 1 identity = new SimplePrincipal(name);

114 }

115 1 Object password = sharedState.get("javax.security.auth.login.password");

116 1 if( password instanceof char[] )

117 0 credential = (char[]) password;

118 1 else if( password != null )

119 {

120 1 String tmp = password.toString();

121 1 credential = tmp.toCharArray();

122 }

123 1 return true;

124 }

125

126 48 super.loginOk = false;

127 48 String[] info = getUsernameAndPassword();

128 48 String username = info[0];

129 48 String password = info[1];

130 48 if( username == null && password == null )

131 {

132 9 identity = unauthenticatedIdentity;

133 9 super.log.trace("Authenticating as unauthenticatedIdentity="+identity);

134 }

135

136 48 if( identity == null )

137 {

138 42 identity = new SimplePrincipal(username);

139 // Hash the user entered password if password hashing is in use

140 42 if( hashAlgorithm != null )

141 4 password = createPasswordHash(username, password);

142 // Validate the password supplied by the subclass

143 42 String expectedPassword = getUsersPassword();

144 42 if( validatePassword(password, expectedPassword) == false )

145 {

146 9 super.log.debug("Bad password for username="+username);

147 9 throw new FailedLoginException("Password Incorrect/Password Required");

148 }

149 }

150

151 39 if( getUseFirstPass() == true )

152 { // Add the username and password to the shared state map

153 0 sharedState.put("javax.security.auth.login.name", username);

154 0 sharedState.put("javax.security.auth.login.password", credential);

155 }

156 39 super.loginOk = true;

157 39 super.log.trace("User '" + identity + "' authenticated, loginOk="+loginOk);

158 39 return true;

159 }

160

161 207 protected Principal getIdentity()

162 {

163 207 return identity;

164 }

165 0 protected Principal getUnauthenticatedIdentity()

166 {

167 0 return unauthenticatedIdentity;

168 }

169

170 0 protected Object getCredentials()

171 {

172 0 return credential;

173 }

174 68 protected String getUsername()

175 {

176 68 String username = null;

177 68 if( getIdentity() != null )

178 68 username = getIdentity().getName();

179 68 return username;

180 }

181

182 /** Called by login() to acquire the username and password strings for

183 authentication. This method does no validation of either.

184 @return String[], [0] = username, [1] = password

185 @exception LoginException thrown if CallbackHandler is not set or fails.

186 */

187 48 protected String[] getUsernameAndPassword() throws LoginException

188 {

189 48 String[] info = {null, null};

190 // prompt for a username and password

191 48 if( callbackHandler == null )

192 {

193 0 throw new LoginException("Error: no CallbackHandler available " +

194 "to collect authentication information");

195 }

196 48 NameCallback nc = new NameCallback("User name: ", "guest");

197 48 PasswordCallback pc = new PasswordCallback("Password: ", false);

198 48 Callback[] callbacks = {nc, pc};

199 48 String username = null;

200 48 String password = null;

201 48 try

202 {

203 48 callbackHandler.handle(callbacks);

204 48 username = nc.getName();

205 48 char[] tmpPassword = pc.getPassword();

206 48 if( tmpPassword != null )

207 {

208 39 credential = new char[tmpPassword.length];

209 39 System.arraycopy(tmpPassword, 0, credential, 0, tmpPassword.length);

210 39 pc.clearPassword();

211 39 password = new String(credential);

212 }

213 }

214 catch(java.io.IOException ioe)

215 {

216 0 throw new LoginException(ioe.toString());

217 }

218 catch(UnsupportedCallbackException uce)

219 {

220 0 throw new LoginException("CallbackHandler does not support: " + uce.getCallback());

221 }

222 48 info[0] = username;

223 48 info[1] = password;

224 48 return info;

225 }

226

227 /**

228 * If hashing is enabled, this method is called from login()

229 * prior to password validation.

230 *



231 * Subclasses may override it to provide customized password hashing,

232 * for example by adding user-specific information or salting.

233 *



234 * The default version calculates the hash based on the following options:

235 *



    236 *
  • hashAlgorithm: The digest algorithm to use.

    237 *
  • hashEncoding: The format used to store the hashes (base64 or hex)

    238 *
  • hashCharset: The encoding used to convert the password to bytes

    239 * for hashing.

    240 *


241 * It will return null if the hash fails for any reason, which will in turn

242 * cause validatePassword() to fail.

243 *

244 * @param username ignored in default version

245 * @param password the password string to be hashed

246 */

247 4 protected String createPasswordHash(String username, String password)

248 {

249 4 String passwordHash = Util.createPasswordHash(hashAlgorithm, hashEncoding,

250 hashCharset, username, password);

251 4 return passwordHash;

252 }

253

254 /** A hook that allows subclasses to change the validation of the input

255 password against the expected password. This version checks that

256 neither inputPassword or expectedPassword are null that that

257 inputPassword.equals(expectedPassword) is true;

258 @return true if the inputPassword is valid, false otherwise.

259 */

260 34 protected boolean validatePassword(String inputPassword, String expectedPassword)

261 {

262 34 if( inputPassword == null || expectedPassword == null )

263 3 return false;

264 31 return inputPassword.equals(expectedPassword);

265 }

266

267 /** Get the expected password for the current username available via

268 the getUsername() method. This is called from within the login()

269 method after the CallbackHandler has returned the username and

270 candidate password.

271 @return the valid password String

272 */

273 abstract protected String getUsersPassword() throws LoginException;

274

275 }

276


»回复此文章   Admin

Re:Re:Re:Re:Re:Re:Re:jboss

作者: hofman  军衔: 上尉  发表时间: 2004-06-21 00:18:26

/*

2 * JBoss, the OpenSource WebOS

3 *

4 * Distributable under LGPL license.

5 * See terms of license at gnu.org.

6 */

7 package org.jboss.security.auth.spi;

8

9 import java.util.*;

10 import java.io.*;

11

12 import java.security.Principal;

13 import java.security.acl.Group;

14 import javax.security.auth.Subject;

15 import javax.security.auth.callback.Callback;

16 import javax.security.auth.callback.CallbackHandler;

17 import javax.security.auth.callback.NameCallback;

18 import javax.security.auth.callback.PasswordCallback;

19 import javax.security.auth.callback.UnsupportedCallbackException;

20 import javax.security.auth.login.LoginException;

21 import javax.security.auth.login.FailedLoginException;

22 import javax.security.auth.spi.LoginModule;

23

24 import org.jboss.logging.Logger;

25 import org.jboss.security.NestableGroup;

26 import org.jboss.security.SimpleGroup;

27

28 /**

29 * This class implements the common functionality required for a JAAS

30 * server side LoginModule and implements the JBossSX standard Subject usage

31 * pattern of storing identities and roles. Subclass this module to create your

32 * own custom LoginModule and override the login(), getRoleSets() and getIdentity()

33 * methods.

34 *



35 * You may also wish to override

36 *



37 * public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)

38 *


39 * In which case the first line of your initialize() method should be:

40 *


41 * super.initialize(subject, callbackHandler, sharedState, options);

42 *


43 *



44 * You may also wish to override

45 *



46 * public boolean login() throws LoginException

47 *


48 * In which case the last line of your login() method should be

49 *


50 * return super.login();

51 *


52 *

53 *@author

54 *@author Scott.Stark@jboss.org

55 *@version $Revision: 1.6.2.2 $

56 */

57 public abstract class AbstractServerLoginModule implements LoginModule

58 {

59 protected Subject subject;

60 protected CallbackHandler callbackHandler;

61 protected Map sharedState;

62 protected Map options;

63 protected Logger log;

64 /** Flag indicating if the shared credential should be used */

65 protected boolean useFirstPass;

66 /** Flag indicating if the login phase succeeded. Subclasses that override

67 the login method must set this to true on successful completion of login

68 */

69 protected boolean loginOk;

70

71 //--- Begin LoginModule interface methods

72 /**

73 * Initialize the login module. This stores the subject, callbackHandler

74 * and sharedState and options for the login session. Subclasses should override

75 * if they need to process their own options. A call to super.initialize(...)

76 * must be made in the case of an override.

77 *



78 * The options are checked for the password-stacking parameter.

79 * If this is set to "useFirstPass", the login identity will be taken from the

80 * javax.security.auth.login.name value of the sharedState map,

81 * and the proof of identity from the

82 * javax.security.auth.login.password value of the sharedState map.

83 *

84 * @param subject the Subject to update after a successful login.

85 * @param callbackHandler the CallbackHandler that will be used to obtain the

86 * the user identity and credentials.

87 * @param sharedState a Map shared between all configured login module instances

88 * @param options the parameters passed to the login module.

89 */

90 56 public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)

91 {

92 56 this.subject = subject;

93 56 this.callbackHandler = callbackHandler;

94 56 this.sharedState = sharedState;

95 56 this.options = options;

96 56 log = Logger.getLogger(getClass());

97 56 log.trace("initialize");

98 /* Check for password sharing options. Any non-null value for

99 password_stacking sets useFirstPass as this module has no way to

100 validate any shared password.

101 */

102 56 String passwordStacking = (String) options.get("password-stacking");

103 56 if( passwordStacking != null && passwordStacking.equalsIgnoreCase("useFirstPass") )

104 1 useFirstPass = true;

105 }

106

107 /** Looks for javax.security.auth.login.name and javax.security.auth.login.password

108 values in the sharedState map if the useFirstPass option was true and returns

109 true if they exist. If they do not or are null this method returns false.

110

111 Note that subclasses that override the login method must set the loginOk

112 ivar to true if the login succeeds in order for the commit phase to

113 populate the Subject. This implementation sets loginOk to true if the

114 login() method returns true, otherwise, it sets loginOk to false.

115 */

116 54 public boolean login() throws LoginException

117 {

118 54 log.trace("login");

119 54 loginOk = false;

120 // If useFirstPass is true, look for the shared password

121 54 if( useFirstPass == true )

122 {

123 1 try

124 {

125 1 Object identity = sharedState.get("javax.security.auth.login.name");

126 1 Object credential = sharedState.get("javax.security.auth.login.password");

127 1 if( identity != null && credential != null )

128 {

129 1 loginOk = true;

130 1 return true;

131 }

132 // Else, fall through and perform the login

133 }

134 catch(Exception e)

135 { // Dump the exception and continue

136 0 log.error("login failed", e);

137 }

138 }

139 53 return false;

140 }

141

142 /** Method to commit the authentication process (phase 2). If the login

143 method completed successfully as indicated by loginOk == true, this

144 method adds the getIdentity() value to the subject getPrincipals() Set.

145 It also adds the members of each Group returned by getRoleSets()

146 to the subject getPrincipals() Set.

147

148 @see javax.security.auth.Subject;

149 @see java.security.acl.Group;

150 @return true always.

151 */

152 47 public boolean commit() throws LoginException

153 {

154 47 log.trace("commit, loginOk="+loginOk);

155 47 if( loginOk == false )

156 2 return false;

157

158 45 Set principals = subject.getPrincipals();

159 45 Principal identity = getIdentity();

160 45 principals.add(identity);

161 45 Group[] roleSets = getRoleSets();

162 45 for(int g = 0; g < roleSets.length; g ++)

163 {

164 57 Group group = roleSets[g];

165 57 String name = group.getName();

166 57 Group subjectGroup = createGroup(name, principals);

167 57 if( subjectGroup instanceof NestableGroup )

168 {

169 /* A NestableGroup only allows Groups to be added to it so we

170 need to add a SimpleGroup to subjectRoles to contain the roles

171 */

172 57 SimpleGroup tmp = new SimpleGroup("Roles");

173 57 subjectGroup.addMember(tmp);

174 57 subjectGroup = tmp;

175 }

176 // Copy the group members to the Subject group

177 57 Enumeration members = group.members();

178 57 while( members.hasMoreElements() )

179 {

180 83 Principal role = (Principal) members.nextElement();

181 83 subjectGroup.addMember(role);

182 }

183 }

184 45 return true;

185 }

186

187 /** Method to abort the authentication process (phase 2).

188 @return true alaways

189 */

190 7 public boolean abort() throws LoginException

191 {

192 7 log.trace("abort");

193 7 return true;

194 }

195

196 /** Remove the user identity and roles added to the Subject during commit.

197 @return true always.

198 */

199 33 public boolean logout() throws LoginException

200 {

201 33 log.trace("logout");

202 // Remove the user identity

203 33 Principal identity = getIdentity();

204 33 Set principals = subject.getPrincipals();

205 33 principals.remove(identity);

206 // Remove any added Groups...

207 33 return true;

208 }

209 //--- End LoginModule interface methods

210

211 // --- Protected methods

212

213 /** Overriden by subclasses to return the Principal that corresponds to

214 the user primary identity.

215 */

216 abstract protected Principal getIdentity();

217 /** Overriden by subclasses to return the Groups that correspond to the

218 to the role sets assigned to the user. Subclasses should create at

219 least a Group named "Roles" that contains the roles assigned to the user.

220 A second common group is "CallerPrincipal" that provides the application

221 identity of the user rather than the security domain identity.

222 @return Group[] containing the sets of roles

223 */

224 abstract protected Group[] getRoleSets() throws LoginException;

225

226 39 protected boolean getUseFirstPass()

227 {

228 39 return useFirstPass;

229 }

230

231 /** Find or create a Group with the given name. Subclasses should use this

232 method to locate the 'Roles' group or create additional types of groups.

233 @return A named Group from the principals set.

234 */

235 57 protected Group createGroup(String name, Set principals)

236 {

237 57 Group roles = null;

238 57 Iterator iter = principals.iterator();

239 57 while( iter.hasNext() )

240 {

241 70 Object next = iter.next();

242 70 if( (next instanceof Group) == false )

243 57 continue;

244 13 Group grp = (Group) next;

245 13 if( grp.getName().equals(name) )

246 {

247 0 roles = grp;

248 0 break;

249 }

250 }

251 // If we did not find a group create one

252 57 if( roles == null )

253 {

254 57 roles = new NestableGroup(name);

255 57 principals.add(roles);

256 }

257 57 return roles;

258 }

259 }

260


»回复此文章   Admin

Re:session

作者: hofman  军衔: 上尉  发表时间: 2004-06-21 08:58:08

You should already have it... hidden in request.getUserPrincipal().getName()
»回复此文章   Admin

Re:Re:session

作者: hofman  军衔: 上尉  发表时间: 2004-06-21 09:05:16

http://www.ejbsolutions.com/products/obox/community/ch18.html

NOTE: These procedures are out of date. If you'd like to have the
latest JBoss automatically installed and integrated with MySQL,
check out our free Community Edition 2.0 product.

Chapter 18. JBoss Configuration Procedures
Table of Contents

All file paths in the instructions below are relative to the server directory under your JBoss installation. If you installed the JBoss/Tomcat bundle, set your current directory to something like /jboss-3.0.3_tomcat-4.1.12/server before proceeding. The procedures below also assume you are using the JBoss "default" configuration. If you're using "all" or "minimal", just change all occurrences of the path prefix "default/" to either "all/" or "/" in the instructions below.

Procedure 18.1. Configuring the Software

JBoss comes pre-configured with support for an embedded database, Hypersonic SQL. If you would like to use MySQL instead, follow these JBoss configuration instructions.

Change or remove the Hypersonic SQL default data source. If you don't need Hypersonic SQL at all, you can just remove default/deploy/hsqldb-service.xml, otherwise change its JndiName so that it's no longer the default. To change its JndiName, do the following:

In default/deploy/hsqldb-service.xml under the ManagedConnectionFactoryName, there's an attribute named JndiName. Change the value from DefaultDS to hsqlDS.

Copy docs/examples/jca/mysql-service.xml to default/deploy and edit it to make the following changes:

Change the server name (to localhost in most cases) and enter your database user name and password (you'll want to use jboss and jboss, if you follow the rest of these instructions). If you already have a MySQL database you want to use, change "jbossdb" to your database name.

Change the DriverClass value from "org.gjt.mm.mysql.Driver" to "com.mysql.jdbc.Driver" because the 3.0.1-beta MySQL JDBC driver has a new class name.

Under the ManagedConnectionFactoryName, there's an attribute named JndiName. Change the value from MySqlDS to DefaultDS to make MySQL your default data source.

Copy your MySQL JDBC driver jar file into default/lib. At present, this is the new 3.0.1-beta series driver called mysql-connector-java-3.0.1-beta-bin.jar

Download the driver.

Extract mysql-connector-java-3.0.1-beta-bin.jar from the archive and copy it to default/lib.

You can now store user password and role information in MySQL.

Add a new policy entry like this to default/conf/login-config.xml:



jboss.security.auth.spi.DatabaseServerLoginModule" flag = "required"]








Start the MySQL shell so we can create our user database, the appropriate tables, and some test data.

Change directories to the MySQL bin directory. If you followed our MySQL installation instructions, this will be /opt/mysql/bin.

Run the following command to start the MySQL shell:

mysql -uroot -ppassword

Create a JBoss user in MySQL with user name "jboss" and password "jboss":

GRANT ALL PRIVILEGES ON jbossdb.* TO 'jboss'@'127.0.0.1/255.255.255.255' IDENTIFIED BY 'jboss';

Quit the MySQL shell:

quit

Restart the MySQL shell, logging in as the new JBoss user:

mysql -ujboss -pjboss

To use MySQL-based authentication and authorization with the policy above, use the following SQL statements to create the database, user, and userrole tables if (they don't already exist) referred to in the policy we just created. Just type them into the MySQL shell and press return after each line:

create database if not exists jbossdb;

use jbossdb;

create table if not exists jbossdb.user
(
email VARCHAR(50) BINARY NOT NULL,
password VARCHAR(50) BINARY NOT NULL,
PRIMARY KEY (email)
) TYPE=InnoDB;

create table if not exists userrole
(
email VARCHAR(50) BINARY NOT NULL,
role VARCHAR(50) BINARY NOT NULL,
INDEX email_index (email),
INDEX role_index (role),
FOREIGN KEY (email) REFERENCES jbossdb.user(email)
) TYPE=InnoDB;

Tip
Note that the user table name is prefixed by the name of our database (in the example, "jbossdb"). Likewise, in the foreign key declaration our "references" table name is prefixed by the name of our database (in the example, "jbossdb"). This is required because MySQL stores its own user information in a table named "user". Without our database specifier, the InnoDB table handler in MySQL (used to provide row-level locking and transactional support) will try to reference the default "user" table instead of ours, which causes an error to occur and the table creation to fail.

Also note that we add "BINARY" to our VARCHAR field definitions. In MySQL, this simply means that we want the field to be case sensitive.


Here are some SQL statements that create users and roles based on the MySQL schema above. Just type them into the MySQL shell and press return after each line:

insert into jbossdb.user values ( "user@company.com", "secret" );
insert into jbossdb.user values ( "admin@company.com", "mypass" );

insert into userrole values ( "user@company.com", "user" );
insert into userrole values ( "admin@company.com", "user" );
insert into userrole values ( "admin@company.com", "admin" );

Exit the MySQL shell by entering:

quit

Procedure 18.2. Authentication and Authorization Alternative

Although not recommended for a production system, it is also possible to authenticate and authorize based on user and role definitions stored in properties files, thus alleviating the need for a database in simple test applications.

Note: Do not follow these steps if you configured users in MySQL using the instructions above.

Edit default/conf/login-config.xml:

Make the unauthenticatedIdentity "guest" for the "other" policy at the bottom by adding this line:





Add a policy similar to the one below if you want to protect your own pages:



jboss.security.auth.spi.UsersRolesLoginModule" flag = "required"/>




In default/conf, create users.properties and roles.properties to specify who can login and what roles they have:

users.properties example:

fred=secret
joe=mypass

roles.properties example:

fred=basic
joe=basic,admin

Procedure 18.3. Security

With users and roles in place, you can use JBoss application security to protect sensitive information. For detailed explanations of the J2EE security architecture, please see these related books.

Now that you have users and roles, you can protect your web applications (HTML, JSP, servlets, etc.) by placing entries similar to the following example at the bottom of your WEB-INF/web.xml above the tag and right before any entries you may have:





Protected Area

/admin/*


admin




BASIC
Administrators Only



admin



Finally, in your jboss-web.xml, you need tell JBoss which security domain to use when protecting your web application. The name you provide here maps to the new application-policy we made a few steps ago in default/conf/login-config.xml. Place an entry similar to the following example above your jboss-web> tag and right before any entries you may have:


java:/jaas/admin


Tip
The application-policy name in login-config.xml is just "admin" but we must prefix that policy name with "java:/jaas/" in jboss-web.xml.

Procedure 18.4. Global Email

JBoss provides applications access to a global email session so that your code doesn't need to provide specific configuration information each time a message is sent.

Edit default/deploy/mail-service.xml to match your incoming and outgoing mail servers. At a minimum, you'll want to set the mail.smtp.host value correctly to enable your server to send email to users, error messages to you, etc. If you've already installed the James mail server as instructed in this documentation, you can use localhost for the mail.smtp.host value.

In your META-INF/ejb-jar.xml file, place the following just above the closing , , or tag of the Enterprise JavaBean (EJB you would like to have access to the email session we defined above.



mail/DefaultMail
javax.mail.Session
Container



Then in your META-INF/jboss.xml file, place the following at the bottom, just above the jboss> tag.




mail/DefaultMail
java:/Mail




Now that all the plumbing is in place, you can send email from your EJB code by using something similar to the following example:


try
{
javax.mail.Session mailSession = (javax.mail.Session) new InitialContext().lookup( "java:comp/env/mail/DefaultMail" );
javax.mail.Message msg = new MimeMessage( mailSession );
msg.setFrom( new InternetAddress( "me@company.com" ) );
msg.setRecipients( javax.mail.Message.RecipientType.TO, InternetAddress.parse( "first.guy@corp.com,other.person@other.org", false ) );
msg.setSubject( "This is a test" );
msg.setContent( "

Hi there!

", "text/html" );
msg.setHeader( "X-Mailer", "JavaMailer" );
msg.setSentDate( new Date() );
Transport.send( msg );
}
catch ( Exception e )
{
LogFactory.getLog( getClass() ).error( "Can't send email", e );
}


Procedure 18.5. CMP

JBoss supports the CMP 2.0 (Container Managed Persistence) specification, which means you can let the container (JBoss) automatically save and load your entity EJB's to and from a database such as MySQL. You don't have to write any tedious JDBC code if you take this route.

Advanced and Enterprise Editions of Out-of-the-Box also include detailed instructions and an implementation of high-end logging and JMS configuration with sample usage code.

The Enterprise Edition goes further to provide production quality performance optimization tips and configuration examples.

To enable JBoss CMP with MySQL, edit default/conf/standardjbosscmp-jdbc.xml:

Change the defaults/datasource-mapping value to mySQL (make sure your case is correct - mySQL).

Change the value of fk-constraint to true.

For details on how to use CMP in general, see our J2EE related books. If you are serious about making JBoss CMP work in a production environment, we also highly recommend that you purchase the JBoss CMP documentation from JBoss.org.