★ 容器启动时,调用监听器StartupListener,初始化Spring上下文环境,设置DAO type,
通过LookupDAOHibernate.getRoles取得角色信息,存于application scope中。
★ 接着时调用UserCounterListener监听器,记录用户的登陆改变。
★ 然后通过容器form认证,将请求转给login.jsp
★ Login.jsp静态引用loginForm.jsp,由其进行主要的权限认证工作。
★ loginForm.jsp表单提交后,将请求转向/authorize
★ /authorize的请求被login servlet处理
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/authorize/*</url-pattern>
</servlet-mapping>
★ 而login servlet的真正处理者是LoginServlet。它取得username和password并且加密password。初始化该servlet时取得初始参数,决定是否采用SSL、密码是否加密和加密规则等,并最后生成请求URL,交由容器认证,此时认证工作并不通过Spring和hibernate,而是直接通过JDBCRealm。采取这样的方式使得SSL加密只是应用于用户登陆,而其他资源认证则使用已有的权限认证系统(毕竟SSL认证是很耗费资源的)。
★StartupListener监听器分析:
public class StartupListener extends ContextLoaderListener
implements ServletContextListener {
private static final Log log = LogFactory.getLog(StartupListener.class);
public void contextInitialized(ServletContextEvent event) {
if (log.isDebugEnabled()) {
log.debug("initializing context...");
}
// call Spring's context ContextLoaderListener to initialize
// all the context files specified in web.xml
super.contextInitialized(event);
ServletContext context = event.getServletContext();
String daoType = context.getInitParameter(Constants.DAO_TYPE);
// if daoType is not specified, use DAO as default
if (daoType == null) {
log.warn("No 'daoType' context carameter, using hibernate");
daoType = Constants.DAO_TYPE_HIBERNATE;
}
// Orion starts Servlets before Listeners, so check if the config
// object already exists
Map config = (HashMap) context.getAttribute(Constants.CONFIG);
if (config == null) {
config = new HashMap();
}
// Create a config object to hold all the app config values
config.put(Constants.DAO_TYPE, daoType);
context.setAttribute(Constants.CONFIG, config);
// output the retrieved values for the Init and Context Parameters
if (log.isDebugEnabled()) {
log.debug("daoType: " + daoType);
log.debug("populating drop-downs...");
}
setupContext(context);
}
public static void setupContext(ServletContext context) {
ApplicationContext ctx =
WebApplicationContextUtils.getRequiredWebApplicationContext(context);
LookupManager mgr = (LookupManager) ctx.getBean("lookupManager");
// get list of possible roles
context.setAttribute(Constants.AVAILABLE_ROLES, mgr.getAllRoles());
if (log.isDebugEnabled()) {
log.debug("drop-down initialization complete [OK]");
}
}
}
★ LoginServlet分析
public final class LoginServlet extends HttpServlet {
private static String authURL = "j_security_check";
private static String httpsPort = null;
private static String httpPort = null;
private static Boolean secure = Boolean.FALSE;
private static String algorithm = "SHA";
private static Boolean encrypt = Boolean.FALSE;
private transient final Log log = LogFactory.getLog(LoginServlet.class);
/**
* 通过web.xml初始化端口参数
*/
private static void initializeSchemePorts(ServletContext servletContext) {
if (httpPort == null) {
String portNumber =
servletContext.getInitParameter(SslUtil.HTTP_PORT_PARAM);
httpPort = ((portNumber == null) ? SslUtil.STD_HTTP_PORT : portNumber);
}
if (httpsPort == null) {
String portNumber =
servletContext.getInitParameter(SslUtil.HTTPS_PORT_PARAM);
httpsPort = ((portNumber == null) ? SslUtil.STD_HTTPS_PORT
: portNumber);
}
}
public void init() throws ServletException {
//web.xml中的auth_url是“j_security_check”
authURL = getInitParameter(Constants.AUTH_URL);
//获得password存储于数据库的加密规则,//web.xml中的algorithm是“SHA”
algorithm = getInitParameter(Constants.ENC_ALGORITHM);
//决定用户是否使用SSL
secure = Boolean.valueOf(getInitParameter("isSecure"));
//password是否被加密
encrypt = Boolean.valueOf(getInitParameter("encrypt-password"));
//默认设置
//Use SSL for login? False
//Programmatic encryption of password ? true
//Use SSL for login? False
//HTTP Port: 8080
//HTTPS Port: 8443
ServletContext ctx = getServletContext();
initializeSchemePorts(ctx);
// Orion starts Servlets before Listeners, so check if the config
// object already exists
Map config = (HashMap) ctx.getAttribute(Constants.CONFIG);
if (config == null) {
config = new HashMap();
}
// update the config object with the init-params from this servlet
config.put(Constants.HTTP_PORT, httpPort);
config.put(Constants.HTTPS_PORT, httpsPort);
config.put(Constants.SECURE_LOGIN, secure);
config.put(Constants.ENC_ALGORITHM, algorithm);
config.put(Constants.ENCRYPT_PASSWORD, encrypt);
ctx.setAttribute(Constants.CONFIG, config);
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
execute(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
execute(request, response);
}
public void execute(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// 如果用户已经登陆,直接转向mainMenu.html
if (request.getRemoteUser() != null) {
if (log.isDebugEnabled()) {
log.debug("User '" + request.getRemoteUser() +
"' already logged in, routing to mainMenu");
}
response.sendRedirect(request.getContextPath() + "/mainMenu.html");
return;
}
String redirectString =
SslUtil.getRedirectString(request, getServletContext(),
secure.booleanValue());
if (redirectString != null) {
// Redirect the page to the desired URL
response.sendRedirect(response.encodeRedirectURL(redirectString));
if (log.isDebugEnabled()) {
log.debug("switching protocols, redirecting user");
}
}
//appfuse默认不使用SSL,会执行下面的程序。
String username = request.getParameter("j_username");
String password = request.getParameter("j_password");
//记录用户信息在Cookies中。
if (request.getParameter("rememberMe") != null) {
request.getSession().setAttribute(Constants.LOGIN_COOKIE, "true");
}
String encryptedPassword = "";
if (encrypt.booleanValue() &&
(request.getAttribute("encrypt") == null)) {
if (log.isDebugEnabled()) {
log.debug("Encrypting password for user '" + username + "'");
}
//加密password
encryptedPassword = StringUtil.encodePassword(password, algorithm);
} else {
encryptedPassword = password;
}
if (redirectString == null) {
// signifies already correct protocol
if (log.isDebugEnabled()) {
log.debug("Authenticating user '" + username + "'");
}
String req =
request.getContextPath() + "/" + authURL + "?j_username=" +
username + "&j_password=" + encryptedPassword + "&j_uri=" +
request.getParameter("j_uri");
// URL : //'/dudu/j_security_check?j_username=mraible&j_password=536c0b339345616c1b33caf4544//54d8b8a19 0d6c&j_uri=’
if (log.isDebugEnabled()) {
log.debug("URL : '" + req + "'");
}
response.sendRedirect(response.encodeRedirectURL(req));
}
}
}