1. 程式人生 > >Java SSH框架系列:使用者登入模組的設計與實現思路

Java SSH框架系列:使用者登入模組的設計與實現思路

1.簡介
使用者登入模組,指的是根據使用者輸入的使用者名稱和密碼,對使用者的身份進行驗證等。如果使用者沒有登入,使用者就無法訪問其他的一些jsp頁面,甚至是action都不能訪問。
二、簡單設計及實現
本程式是基於Java的SSH框架進行的。
1.資料庫設計

我們應該設計一個使用者表,其Userinfo表,對應的SQL語句為(Oracle資料庫):

create table userinfo
(
  id                        varchar2(36) primary key,
  username                 varchar2(50) not null,
  password                 varchar2(50) not null,
  auth_limit               varchar2(10) not null,
  register_time           varchar2(40),
  create_time             varchar2(40),
  remarks                  varchar2(1024)               
);

分別是id,也就是UUID,使用者名稱、密碼、許可權、註冊時間、記錄插入資料庫的時間、備註等幾個欄位。

2.使用MyEclipse的Hibernate逆向工具,生成對應的Java Bean和相應的hibernate的xml配置檔案Userinfo.hbm.xml

package edu.njupt.zhb.bean;

/**
 * Userinfo entity. @author MyEclipse Persistence Tools
 */

public class Userinfo implements java.io.Serializable {

	// Fields

	private String id;
	private String username;
	private String password;
	private String authLimit;
	private String registerTime;
	private String createTime;
	private String remarks;

	// Constructors

	/** default constructor */
	public Userinfo() {
	}

	/** minimal constructor */
	public Userinfo(String id, String username, String password,
			String authLimit) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.authLimit = authLimit;
	}

	/** full constructor */
	public Userinfo(String id, String username, String password,
			String authLimit, String registerTime, String createTime,
			String remarks) {
		this.id = id;
		this.username = username;
		this.password = password;
		this.authLimit = authLimit;
		this.registerTime = registerTime;
		this.createTime = createTime;
		this.remarks = remarks;
	}

	// Property accessors

	public String getId() {
		return this.id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getUsername() {
		return this.username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return this.password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getAuthLimit() {
		return this.authLimit;
	}

	public void setAuthLimit(String authLimit) {
		this.authLimit = authLimit;
	}

	public String getRegisterTime() {
		return this.registerTime;
	}

	public void setRegisterTime(String registerTime) {
		this.registerTime = registerTime;
	}

	public String getCreateTime() {
		return this.createTime;
	}

	public void setCreateTime(String createTime) {
		this.createTime = createTime;
	}

	public String getRemarks() {
		return this.remarks;
	}

	public void setRemarks(String remarks) {
		this.remarks = remarks;
	}

}

3.新建一個UserInfoService介面
/*
 * $filename: VideoInfoService.java,v $
 * $Date: 2014-1-2  $
 * Copyright (C) ZhengHaibo, Inc. All rights reserved.
 * This software is Made by Zhenghaibo.
 */
package edu.njupt.zhb.service;

import edu.njupt.zhb.bean.Userinfo;

/*
 *@author: ZhengHaibo  
 *web:     http://blog.csdn.net/nuptboyzhb
 *GitHub   https://github.com/nuptboyzhb
 *mail:    
[email protected]
*2014-1-2 Nanjing,njupt,China */ public interface UserInfoService { String getUserInfoList(int page, int rows); String addUser(Userinfo userinfo); String deleteUser(String userId); String editUser(Userinfo userinfo); Userinfo getUserInfoByName(String username); }

4.新建一個UserInfoServiceImpl類
/*
 * $filename: VideoInfoServiceImpl.java,v $
 * $Date: 2014-1-2  $
 * Copyright (C) ZhengHaibo, Inc. All rights reserved.
 * This software is Made by Zhenghaibo.
 */
package edu.njupt.zhb.service.impl;

import java.util.ArrayList;
import java.util.List;

import net.sf.json.JSONObject;

import edu.njupt.zhb.bean.Userinfo;
import edu.njupt.zhb.dao.BaseDao;
import edu.njupt.zhb.service.UserInfoService;
import edu.njupt.zhb.utils.DataGrid;
import edu.njupt.zhb.utils.Tips;
import edu.njupt.zhb.view.ViewUser;

/*
 *@author: ZhengHaibo  
 *web:     http://blog.csdn.net/nuptboyzhb
 *GitHub   https://github.com/nuptboyzhb
 *mail:    [email protected]
 *2014-1-2  Nanjing,njupt,China
 */
public class UserInfoServiceImpl implements UserInfoService{
	private BaseDao<Userinfo> userinfoBaseDao;
	public BaseDao<Userinfo> getUserBaseDao() {
		return userinfoBaseDao;
	}
	public void setUserBaseDao(BaseDao<Userinfo> userinfoBaseDao) {
		this.userinfoBaseDao = userinfoBaseDao;
	}
	@Override
	public String getUserInfoList(int page, int rows) {
		// TODO Auto-generated method stub
		System.out.println("page="+page+",rows="+rows);
		String hql = "from Userinfo";
		try {
			List<Userinfo> list = userinfoBaseDao.find(hql,page,rows);
			List<ViewUser> resultList = new ArrayList<ViewUser>();
			for(Userinfo userinfo:list){
				ViewUser viewUser = new ViewUser();
				viewUser.setBz(userinfo.getRemarks());
				viewUser.setId(userinfo.getId());
				viewUser.setPwd(userinfo.getPassword());
				viewUser.setYhm(userinfo.getUsername());
				viewUser.setYhqx(userinfo.getAuthLimit());
				viewUser.setZcsj(userinfo.getRegisterTime());
				resultList.add(viewUser);
			}
			DataGrid<ViewUser> dataGrid = new DataGrid<ViewUser>();
			dataGrid.setRows(resultList);
			dataGrid.setTotal(userinfoBaseDao.total(hql));
			String result = JSONObject.fromObject(dataGrid).toString();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	public String addUser(Userinfo userinfo) {
		// TODO Auto-generated method stub
		Tips tips = new Tips();
		String hql = "from Userinfo where username = '"+userinfo.getUsername()+"'";
		try {
			List<Userinfo> list =  userinfoBaseDao.find(hql);
			if(list!=null&&list.size()>0){
				tips.setMsg("新增失敗!使用者名稱已經存在!");
				return JSONObject.fromObject(tips).toString();
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			userinfoBaseDao.save(userinfo);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			tips.setMsg("新增失敗");
			return JSONObject.fromObject(tips).toString();
		}
		tips.setMsg("新增成功");
		return JSONObject.fromObject(tips).toString();
	}
	public String deleteUser(String userid) {
		// TODO Auto-generated method stub
		Tips tips = new Tips();
		try {
			userinfoBaseDao.executeHql("delete from Userinfo where id = '"+userid+"'");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			tips.setMsg("刪除失敗");
			return JSONObject.fromObject(tips).toString();
		}
		tips.setMsg("刪除成功");
		return JSONObject.fromObject(tips).toString();
	}
	public String editUser(Userinfo userinfo) {
		// TODO Auto-generated method stub
		Tips tips = new Tips();
		String hql = "from Userinfo where username = '"+userinfo.getUsername()+"'";
		try {
			List<Userinfo> list =  userinfoBaseDao.find(hql);
			if(list!=null&&list.size()>0){
				tips.setMsg("更新失敗!使用者名稱已經存在!");
				return JSONObject.fromObject(tips).toString();
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			userinfoBaseDao.update(userinfo);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			tips.setMsg("編輯失敗");
			return JSONObject.fromObject(tips).toString();
		}
		tips.setMsg("編輯成功");
		return JSONObject.fromObject(tips).toString();
	}
	@Override
	public Userinfo getUserInfoByName(String username) {
		// TODO Auto-generated method stub
		Userinfo  userinfo = null;
		String hql = "from Userinfo where username = '"+username+"'";
		try {
			List<Userinfo> list =  userinfoBaseDao.find(hql);
			if(list == null || list.size()==0){
				return null;
			}
			userinfo = list.get(0);
			return userinfo;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

}

5.新建UserInfoAction類,(當然,我們的BaseAction肯定是從struts2中的ActionSupport派生出來的)
/*
 * $filename: VideoInfoAction.java,v $
 * $Date: 2014-1-2  $
 * Copyright (C) ZhengHaibo, Inc. All rights reserved.
 * This software is Made by Zhenghaibo.
 */
package edu.njupt.zhb.action;

import java.util.UUID;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import edu.njupt.zhb.bean.Userinfo;
import edu.njupt.zhb.service.UserInfoService;
import edu.njupt.zhb.utils.Tips;
import edu.njupt.zhb.utils.TipsMsg;
import edu.njupt.zhb.utils.Utils;

/*
 *@author: ZhengHaibo  
 *web:     http://blog.csdn.net/nuptboyzhb
 *GitHub   https://github.com/nuptboyzhb
 *mail:    [email protected]
 *2014-1-2  Nanjing,njupt,China
 */
public class UserInfoAction extends BaseAction {

	/**
	 * 
	 */
	private static final long serialVersionUID = 3321845277376234101L;
	private Userinfo userinfo;
	private String userId;
	private String username;
	private String password;
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getUserId() {
		return userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}

	public Userinfo getUserinfo() {
		return userinfo;
	}

	public void setUserinfo(Userinfo userinfo) {
		this.userinfo = userinfo;
	}

	private UserInfoService userInfoService;

	public UserInfoService getUserInfoService() {
		return userInfoService;
	}

	public void setUserInfoService(UserInfoService userInfoService) {
		this.userInfoService = userInfoService;
	}

	public void getUserInfoList() {
		String jsonResult = userInfoService.getUserInfoList(page, rows);
		System.out.println(jsonResult);
		super.writeStr(jsonResult);
	}

	/**
	 * 新增使用者
	 * 
	 * @return
	 */
	public void addUser() {
		if (userinfo == null) {
			Tips tips = new Tips();
			tips.setMsg("新增失敗!物件為空");
			getPrintWriter().write(JSONArray.fromObject(tips).toString());
			return;
		}
		userinfo.setId(UUID.randomUUID() + "");
		userinfo.setCreateTime(Utils.getNowTime());
		String jsonResult = userInfoService.addUser(userinfo);
		getPrintWriter().write(jsonResult);
	}

	/**
	 * 刪除使用者
	 * 
	 * @return
	 */
	public void deleteUser() {
		if (userId == null) {
			Tips tips = new Tips();
			tips.setMsg("刪除失敗!學號無效");
			getPrintWriter().write(JSONArray.fromObject(tips).toString());
			return;
		}
		String jsonResult = userInfoService.deleteUser(userId);
		getPrintWriter().write(jsonResult);
	}

	/**
	 * 編輯使用者
	 * 
	 * @return
	 */
	public void editUser() {
		if (userinfo == null) {
			Tips tips = new Tips();
			tips.setMsg("編輯失敗!物件為空");
			getPrintWriter().write(JSONArray.fromObject(tips).toString());
			return;
		}
		userinfo.setId(userId);
		String jsonResult = userInfoService.editUser(userinfo);
		getPrintWriter().write(jsonResult);
	}

	public void login() {
		TipsMsg tipsMsg = new TipsMsg();
		if(username==null){
			tipsMsg.setId("1");
			tipsMsg.setMsg("使用者名稱為空!");
			String result = JSONObject.fromObject(tipsMsg).toString();
			super.writeStr(result);
			return;
		}
		Userinfo userinfo = userInfoService.getUserInfoByName(username);
		if(userinfo==null){
			tipsMsg.setId("1");
			tipsMsg.setMsg("使用者名稱不存在");
			String result = JSONObject.fromObject(tipsMsg).toString();
			super.writeStr(result);
			return;
		}
		if(!userinfo.getPassword().equals(password)){
			tipsMsg.setId("1");
			tipsMsg.setMsg("使用者名稱或密碼錯誤");
			String result = JSONObject.fromObject(tipsMsg).toString();
			super.writeStr(result);
			return;
		}
		super.setCurrentUser(userinfo);
		tipsMsg.setId("2");
		tipsMsg.setMsg("登入成功");
		String result = JSONObject.fromObject(tipsMsg).toString();
		super.writeStr(result);
		return;
	}
}

6.配置Spring的applicationContext.xml檔案,依次注入Dao、Service和Action。
<bean id="baseDao" class="edu.njupt.zhb.dao.BaseDao">
    	<property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

<bean id="userInfoService" class="edu.njupt.zhb.service.impl.UserInfoServiceImpl">
    	<property name="userBaseDao" ref="baseDao"></property>
    </bean>

<bean id="userInfoAction" class="edu.njupt.zhb.action.UserInfoAction" scope="prototype">
		<property name="userInfoService" ref="userInfoService"></property>
	</bean>

7.配置Spring的Hibernate的Java Bean的對映檔案
<bean id="sessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="hibernateProperties">
			<props>
			    <prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
				<prop key="current_session_context_class">thread</prop>
			</props>
		</property>
		<property name="mappingResources">
			<list>
			    ...
				<value>edu/njupt/zhb/bean/Userinfo.hbm.xml</value>
			</list>
		</property>
	</bean>

8.配置Struts2檔案,增加相應的Action,login等
<action name="getUserInfoList" class="userInfoAction" method="getUserInfoList"></action>
		<action name="addUser" class="userInfoAction" method="addUser"></action>
		<action name="deleteUser" class="userInfoAction" method="deleteUser"></action>
		<action name="editUser" class="userInfoAction" method="editUser"></action>
		<action name="login" class="userInfoAction" method="login"></action>

9.登入頁面login.jsp中的ajax請求:
 $("#btnLogin").click(function(){
			var message = "";
			var userName=$('input[name="userName"]').val();
			var userPass=$('input[name="userPass"]').val();
			if(userName == ""){
				alert("請輸入使用者名稱!");
				return;
			}else if(userPass == ""){
				alert("請輸入密碼!");
				return;
			}
			$.ajax({
                type:"post",
                url:'login.action?username='+userName+'&password='+userPass,
                dateType:"json",
                success:function(data){
				    var json=eval("("+data+")");
                    if(json.id=='1'){
                	   alert(json.msg);
                	   return;
                    }else{
                	   $("#frm").submit();
                    }
			    }
            });
        });

10.為了防止使用者在不登陸的情況下,訪問其他頁面,我們在每一個jsp頁面中新增如下程式碼:
<%if(null == request.getSession().getAttribute("user")){
		   response.getWriter().write("<script>window.location.href = 'login.jsp'</script>");
	   }
     %>

思考
完成上述複雜的功能之後,我們是不是就完成任務了呢?顯然不是,我們經過測試我們就會發現,在我們沒有登入的情況下,我們確實沒有辦法訪問其他的JSP頁面,但是我們可以直接在瀏覽器中訪問struts中配置的action!這依然有很大的漏洞,那我們怎麼對action進行攔截呢?也即是對非login.action進行攔截判斷,如果使用者已經登入,就正常登入,如果使用者沒有登入,就返回login,讓其登入。因此我們需要使用struts2的攔截器。

11.攔截器的Java程式碼

/*
 * $filename: CheckLoginInterceptor.java,v $
 * $Date: 2014-1-15  $
 * Copyright (C) ZhengHaibo, Inc. All rights reserved.
 * This software is Made by Zhenghaibo.
 */
package edu.njupt.zhb.utils;

import java.util.Map;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;

import edu.njupt.zhb.action.UserInfoAction;
import edu.njupt.zhb.bean.Userinfo;

/*
 *@author: ZhengHaibo  
 *web:     http://blog.csdn.net/nuptboyzhb
 *GitHub   https://github.com/nuptboyzhb
 *mail:    [email protected]
 *2014-1-15  Nanjing,njupt,China
 */
public class CheckLoginInterceptor extends AbstractInterceptor{

	/**
	 * 
	 */
	private static final long serialVersionUID = 2092930262572782343L;

	@Override
	public String intercept(ActionInvocation actionInvocation) throws Exception {
		// TODO Auto-generated method stub
		//System.out.println("begin check login interceptor!");
        // 對LoginAction不做該項攔截
        Object action = actionInvocation.getAction();
        if (action instanceof UserInfoAction) {
            //System.out.println("exit check login, because this is login action.");
            //UserInfoAction userinfoAction = (UserInfoAction)action;
            return actionInvocation.invoke();
        }
        // 確認Session中是否存在User
        Map<String,Object> session = actionInvocation.getInvocationContext().getSession();
        Userinfo user = (Userinfo) session.get("user");
        if (user != null) {
            // 存在的情況下進行後續操作。
            //System.out.println("already login!");
            return actionInvocation.invoke();
        } else {
            // 否則終止後續操作,返回LOGIN
            System.out.println("no login, forward login page!");
            return "login";
        }
	}

}

12.攔截器在Struts2中的配置
<interceptor name="loginIntercepter"
				class="edu.njupt.zhb.utils.CheckLoginInterceptor">
			</interceptor>
			<!-- 攔截器棧 -->
			<interceptor-stack name="loginStack">
				<interceptor-ref name="defaultStack" />
				<interceptor-ref name="loginIntercepter" />
			</interceptor-stack>

為每一個action配置攔截器,比如:
<action name="getStudentList" class="dataGridDemoAction"
			method="getStudentList">
			<result type="httpheader"></result>
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>
		<action name="addStudent" class="dataGridDemoAction" method="addStudent">
			<result type="httpheader"></result>
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>
		<action name="deleteStudent" class="dataGridDemoAction" method="deleteStudent">
			<result type="httpheader"></result>
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>
		<action name="editStudent" class="dataGridDemoAction" method="editStudent">
			<result type="httpheader"></result>
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>

當然,對於使用者登入的action,我們也配置相應的攔截器:
		<!-- 使用者資訊Action -->
		<action name="getUserInfoList" class="userInfoAction" method="getUserInfoList">
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>
		<action name="addUser" class="userInfoAction" method="addUser">
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>
		<action name="deleteUser" class="userInfoAction" method="deleteUser">
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>
		<action name="editUser" class="userInfoAction" method="editUser">
			<interceptor-ref name="loginStack" />
			<result name="login">/login.jsp</result>
		</action>
		<action name="login" class="userInfoAction" method="login">
			<interceptor-ref name="loginStack" />
		</action>

總結:

以上步驟完成之後,我們基本上就完成了一個簡單的使用者登入模組的設計和實現了。而且我們可以根據使用者的許可權,顯示不同的內容。比如管理員和普通操作員等具有不同的操作許可權。

說明:

以上程式碼只是一些程式碼片段,我這裡主要介紹的是思路。以上程式碼還有一些小小的漏洞,比如,我們在沒有登入的情況下,還是可以直接訪問getUserinfoList、deleteUser、editUser等與login在同一個Action類中的action。因此,我們設計的時候,要儘量將login和logout單獨一個action。還有:上面寫的getUserinfoList等一些action,目的是管理員對使用者表進行增刪改查等操作的,和本部落格關係不大。本文主要注意的是:一個是在jsp頁面中對未登陸使用者的攔截,還有就是通過配置Struts2的攔截器,對未登入使用者直接訪問action方式進行攔截的。

未經允許,不得用於商業目的