JAASUserPrincipal.java
package com.rainyday.server.login;
import java.io.Serializable;
import java.security.Principal;
/**
* @author semika
*
*/
public class JAASUserPrincipal implements Principal, Serializable {
private String name;
/**
* @param name
*/
public JAASUserPrincipal(String name) {
if (name == null) {
throw new NullPointerException("NULL user name");
}
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return "UserPrincipal [name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
JAASUserPrincipal other = (JAASUserPrincipal) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
JAASRolePrincipal.java
package com.rainyday.server.login;
import java.io.Serializable;
import java.security.Principal;
/**
* @author semika
*
*/
public class JAASRolePrincipal implements Principal, Serializable {
private String name;
/**
* @param name
*/
public JAASRolePrincipal(String name) {
if (name == null) {
throw new NullPointerException("NULL role name");
}
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public String toString() {
return "JASSRolePrincipal [name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
JAASRolePrincipal other = (JAASRolePrincipal) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
JAASPasswordPrincipal.java
package com.rainyday.server.login;
import java.io.Serializable;
import java.security.Principal;
/**
* @author semika
*
*/
public class JAASPasswordPrincipal implements Principal, Serializable {
private String name;
/**
* @param name
*/
public JAASPasswordPrincipal(String name) {
if (name == null) {
throw new NullPointerException("NULL password.");
}
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
JAASPasswordPrincipal other = (JAASPasswordPrincipal) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "JAASPasswordPrincipal [name=" + name + "]";
}
}
rainyDay {
com.rainyday.server.login.JAASLoginModule required
dbDriver="com.mysql.jdbc.Driver"
dbURL="jdbc:mysql://localhost/rainyday"
dbUser="root"
dbPassword="abc123"
userQuery="select username from secu_user where secu_user.username=? and secu_user.password=?"
roleQuery="select secu_user_role.rolename from secu_user, secu_user_role "
+ "where secu_user.username=secu_user_role.username and secu_user.username=?"
debug=true;
};
“ jass.config”文件应具有类似的格式。 除了登录模块声明之外,您还可以根据需要声明选项。 登录模块可以在initialize()方法参数中使用“选项”映射来使用这些选项。
另外,我们应该通过添加JAVA_OPTS环境变量的路径来告诉tomcat,“ jaas.config”文件的位置。 我将其添加到$ CATALINA_HOME / bin下的'catalina.sh'文件中,如下所示。
JAVA_OPTS =” $ JAVA_OPTS -Djava.security.auth.login.config == .. / conf / jaas.config”
接下来,您需要声明JAASRealm配置。 您可以在$ CATALINA_HOME / conf下的server.xml文件中添加一个新的“ Realm”条目。 在我们的教程中,“领域”条目如下。
<Realm className="org.apache.catalina.realm.JAASRealm"
appName="rainyDay"
userClassNames="com.rainyday.server.login.JASSUserPrincipal,com.rainyday.server.login.JAASPasswordPrincipal"
roleClassNames="com.rainyday.server.login.JASSRolePrincipal"/>
有关apache tomcat的领域配置,您可以查看此文档 。 jaas登录模块的完整源代码。
JAASLoginModule.java
package com.rainyday.server.login;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;
import org.apache.log4j.Logger;
/**
* @author semika
*
*/
public class JAASLoginModule implements LoginModule {
private static Logger LOGGER = Logger.getLogger(JAASLoginModule.class);
// initial state
private Subject subject;
private CallbackHandler callbackHandler;
private Map sharedState;
private Map options;
// configurable option
private boolean debug = false;
// the authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;
//user credentials
private String username = null;
private char[] password = null;
//user principle
private JAASUserPrincipal userPrincipal = null;
private JAASPasswordPrincipal passwordPrincipal = null;
public JAASLoginModule() {
super();
}
@Override
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map<string, ?=""> sharedState, Map<string, ?=""> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.sharedState = sharedState;
this.options = options;
debug = "true".equalsIgnoreCase((String)options.get("debug"));
}
@Override
public boolean login() throws LoginException {
if (callbackHandler == null){
throw new LoginException("Error: no CallbackHandler available " +
"to garner authentication information from the user");
}
Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("username");
callbacks[1] = new PasswordCallback("password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback)callbacks[0]).getName();
password = ((PasswordCallback)callbacks[1]).getPassword();
if (debug) {
LOGGER.debug("Username :" + username);
LOGGER.debug("Password : " + password);
}
if (username == null || password == null) {
LOGGER.error("Callback handler does not return login data properly");
throw new LoginException("Callback handler does not return login data properly");
}
if (isValidUser()) { //validate user.
succeeded = true;
return true;
}
} catch (IOException e) {
e.printStackTrace();
} catch (UnsupportedCallbackException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean commit() throws LoginException {
if (succeeded == false) {
return false;
} else {
userPrincipal = new JAASUserPrincipal(username);
if (!subject.getPrincipals().contains(userPrincipal)) {
subject.getPrincipals().add(userPrincipal);
LOGGER.debug("User principal added:" + userPrincipal);
}
passwordPrincipal = new JAASPasswordPrincipal(new String(password));
if (!subject.getPrincipals().contains(passwordPrincipal)) {
subject.getPrincipals().add(passwordPrincipal);
LOGGER.debug("Password principal added: " + passwordPrincipal);
}
//populate subject with roles.
List<string> roles = getRoles();
for (String role: roles) {
JAASRolePrincipal rolePrincipal = new JAASRolePrincipal(role);
if (!subject.getPrincipals().contains(rolePrincipal)) {
subject.getPrincipals().add(rolePrincipal);
LOGGER.debug("Role principal added: " + rolePrincipal);
}
}
commitSucceeded = true;
LOGGER.info("Login subject were successfully populated with principals and roles");
return true;
}
}
@Override
public boolean abort() throws LoginException {
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
succeeded = false;
username = null;
if (password != null) {
password = null;
}
userPrincipal = null;
} else {
logout();
}
return true;
}
@Override
public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
succeeded = commitSucceeded;
username = null;
if (password != null) {
for (int i = 0; i < password.length; i++){
password[i] = ' ';
password = null;
}
}
userPrincipal = null;
return true;
}
private boolean isValidUser() throws LoginException {
String sql = (String)options.get("userQuery");
Connection con = null;
ResultSet rs = null;
PreparedStatement stmt = null;
try {
con = getConnection();
stmt = con.prepareStatement(sql);
stmt.setString(1, username);
stmt.setString(2, new String(password));
rs = stmt.executeQuery();
if (rs.next()) { //User exist with the given user name and password.
return true;
}
} catch (Exception e) {
LOGGER.error("Error when loading user from the database " + e);
e.printStackTrace();
} finally {
try {
rs.close();
} catch (SQLException e) {
LOGGER.error("Error when closing result set." + e);
}
try {
stmt.close();
} catch (SQLException e) {
LOGGER.error("Error when closing statement." + e);
}
try {
con.close();
} catch (SQLException e) {
LOGGER.error("Error when closing connection." + e);
}
}
return false;
}
/**
* Returns list of roles assigned to authenticated user.
* @return
*/
private List<string> getRoles() {
Connection con = null;
ResultSet rs = null;
PreparedStatement stmt = null;
List<string> roleList = new ArrayList<string>();
try {
con = getConnection();
String sql = (String)options.get("roleQuery");
stmt = con.prepareStatement(sql);
stmt.setString(1, username);
rs = stmt.executeQuery();
if (rs.next()) {
roleList.add(rs.getString("rolename"));
}
} catch (Exception e) {
LOGGER.error("Error when loading user from the database " + e);
e.printStackTrace();
} finally {
try {
rs.close();
} catch (SQLException e) {
LOGGER.error("Error when closing result set." + e);
}
try {
stmt.close();
} catch (SQLException e) {
LOGGER.error("Error when closing statement." + e);
}
try {
con.close();
} catch (SQLException e) {
LOGGER.error("Error when closing connection." + e);
}
}
return roleList;
}
/**
* Returns JDBC connection
* @return
* @throws LoginException
*/
private Connection getConnection() throws LoginException {
String dBUser = (String)options.get("dbUser");
String dBPassword = (String)options.get("dbPassword");
String dBUrl = (String)options.get("dbURL");
String dBDriver = (String)options.get("dbDriver");
Connection con = null;
try {
//loading driver
Class.forName (dBDriver).newInstance();
con = DriverManager.getConnection (dBUrl, dBUser, dBPassword);
}
catch (Exception e) {
LOGGER.error("Error when creating database connection" + e);
e.printStackTrace();
} finally {
}
return con;
}
}
<login-config>
<auth-method>FORM</auth-method>
<realm-name>rainyDay</realm-name>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/error.jsp</form-error-page>
</form-login-config>
</login-config>
<security-role>
<role-name>*</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Rainy day</web-resource-name>
<url-pattern>/</url-pattern>
<http-method>POST</http-method>
<http-method>GET</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
由于上述安全限制,如果在没有身份验证的情况下对应用程序受保护区域中的特定资源提出了某些请求,则该请求将被重定向到“登录”页面。 接下来,我将向您展示简单HTML表单,该表单将在表单提交后调用我们的登录模块。
<form id="loginForm" name="loginForm" method="post" action="j_security_check">
User Name : <input id="username" type="text" name="j_username" class="textbox"></input>
Password : <input id="password" type="password" name="j_password" class="textbox"></input>
<input name="login" type="submit" value="LOGIN" id="submit" class="button blue">
</form>
JAASCallbackHandler.java
package com.rainyday.server.login;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.log4j.Logger;
/**
* @author semika
*
*/
public class JAASCallbackHandler implements CallbackHandler {
private static final Logger LOGGER = Logger.getLogger(JAASCallbackHandler.class);
private String username = null;
private String password = null;
/**
* @param username
* @param password
*/
public JAASCallbackHandler(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
LOGGER.info("Callback Handler invoked ");
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback nameCallback = (NameCallback) callbacks[i];
nameCallback.setName(username);
} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback passwordCallback = (PasswordCallback) callbacks[i];
passwordCallback.setPassword(password.toCharArray());
} else {
throw new UnsupportedCallbackException(callbacks[i], "The submitted Callback is unsupported");
}
}
}
}
接下来,我们必须创建一个' LoginContext'实例来显式调用身份验证。
LoginContext lc = null;
try {
lc = new LoginContext("rainyDay", new JAASCallbackHandler(username, password));
lc.login();
//get the subject.
Subject subject = lc.getSubject();
//get principals
subject.getPrincipals();
LOGGER.info("established new logincontext");
} catch (LoginException e) {
LOGGER.error("Authentication failed " + e);
}
参考:来自我们JCG合作伙伴 Semika loku kaluge的基于Java表单的身份验证 ,位于Code Box博客上。
翻译自: https://www.javacodegeeks.com/2012/06/java-jaas-form-based-authentication.html
本文详述了如何使用 Java Authentication and Authorization Service (JAAS) 实现表单认证,包括创建登录模块类、配置 JAAS 和 Tomcat 的步骤,以及如何在 web.xml 中配置安全约束。

5456

被折叠的 条评论
为什么被折叠?



