一 模块需求细化
登录的用户,默认情况有三个不同角色,分别为:系统管理员,前台客服,信息管理员。
用户登录后能够根据其角色来进行相关工作,进行完工作需要能够注销。
细化需求如下:
用户登录之后按角色分配权限信息。
登录日志表自动保存登录信息。
需要把权限信息保存在Session中。
需要登录检测,Session中保存用户id和最后登录时间等。
在member表中也需要更新最后一次登录的日期时间。
用户登录后可进行密码修改。
需要用户登录后能够注销。
二 模块相关数据库实现细节

1 输入数据表
用户表:member
角色:role
角色-权限关系表:role-groups
权限组-权限关系表:groups-action
权限组表:group
权限表:action
2 输出数据表
用户登录日志表:logs
用户:member
三 用户登录界面设计
1 登录页面代码login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
String loginUrl = basePath + "LoginServletBack/login" ;
System.out.println(loginUrl);
%>
<html>
<head>
<base href="<%=basePath%>">
<title>CRM管理系统</title>
<jsp:include page="/pages/plugins/import_file.jsp"/>
<script src="<%=basePath%>js/cloud.js" type="text/javascript"></script>
<script src="<%=basePath%>js/login.js" type="text/javascript"></script>
</head>
<body style="background-color:#df7611; background-image:url(images/light.png); background-repeat:no-repeat; background-position:center top; overflow:hidden;">
<div id="mainBody">
<div id="cloud1" class="cloud"></div>
<div id="cloud2" class="cloud"></div>
</div>
<div class="logintop">
<span>欢迎登录CRM管理界面平台</span>
<ul>
<li><a href="#">回首页</a></li>
<li><a href="#">帮助</a></li>
<li><a href="#">关于</a></li>
</ul>
</div>
<div class="loginbody">
<span class="systemlogo"></span>
<div class="loginbox">
<form action="<%=loginUrl%>" method="post" id="loginForm">
<ul>
<li><input name="member.mid" id="member.mid" type="text" class="loginuser"
placeholder="输入登录用户名"/></li>
<li><input name="member.password" id="member.password" type="password" class="loginpwd"
placeholder="请输入登录密码"/></li>
<li><input type="submit" class="loginbtn" value="登录"/></li>
</ul>
</form>
</div>
</div>
<div class="loginbm"></div>
</body>
</html>2 界面展示

3 输入用户名和密码,点击确认
会跳到loginUrl去处理,loginUrl的值为:http://localhost:8080/CRMProject/LoginServletBack/login
LoginServletBack/login的处理入口见控制层代码。
4 如果用户名和密码正确,控制层代码会将页面转向/pages/back/index.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<html>
<head>
<base href="<%=basePath%>">
<title>CRM管理系统</title>
<jsp:include page="/pages/plugins/import_file.jsp"/>
</head>
<frameset rows="88,*,31" cols="*" frameborder="no" border="0"
framespacing="0">
<frame src="<%=basePath%>pages/back/top.jsp" name="topFrame" scrolling="No"
noresize="noresize" id="topFrame" title="topFrame" />
<frameset cols="187,*" frameborder="no" border="0" framespacing="0">
<frame src="<%=basePath%>pages/back/left.jsp" name="leftFrame" scrolling="No"
noresize="noresize" id="leftFrame" title="leftFrame" />
<frame src="<%=basePath%>pages/back/DefaultServletBack/show" name="rightFrame" id="rightFrame"
title="rightFrame" />
</frameset>
<frame src="<%=basePath%>pages/back/footer.jsp" name="bottomFrame" scrolling="No"
noresize="noresize" id="bottomFrame" title="bottomFrame" />
</frameset>
<noframes>
</html>5 该页面展示如下:

上面代码关键是<%=basePath%>pages/back/DefaultServletBack/show,它是控制层实现的。我们去控制层分析该代码。
四 控制层模块设计实现
1 登录控制层代码实现
package cn.mldn.crm.servlet.back;
import java.util.Map;
import javax.servlet.annotation.WebServlet;
import cn.mldn.crm.factory.ServiceFactory;
import cn.mldn.crm.vo.Member;
import cn.mldn.util.MD5Code;
import cn.mldn.util.servlet.DispatcherServlet;
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/LoginServletBack/*") // 当点击登录代码,会走到这里
public class LoginServletBack extends DispatcherServlet {
private Member member = new Member();
public String login() {
// 把输入的密码加密成功后设置给password属性
this.member.setPassword(new MD5Code().getMD5ofStr(this.member.getPassword()));
try {
// 调用业务层获得用户相关信息
Map<String,Object> map = ServiceFactory.getIMemberServiceBackInstance().login(this.member) ;
boolean loginFlag = (Boolean) map.get("flag") ;
if (loginFlag) {
super.getSession().setAttribute("mid", this.member.getMid());
super.getSession().setAttribute("flag", this.member.getFlag());
super.getSession().setAttribute("groups", this.member.getRole().getGroups());
super.getSession().setAttribute("allActions", map.get("allActions"));
super.getSession().setAttribute("unread", map.get("unread"));
// 处理权限问题;
super.setMsgAndUrl("member.login.success", "index.page");
} else {
super.setMsgAndUrl("member.login.failure", "member.login.page");
}
} catch (Exception e) {
e.printStackTrace();
}
return "forward.page" ;
}
......
}2 在上面这个函数中,如果密码输入正确,会跳到index.page去处理,并携带消息member.login.success
这里index.page为index.page=/pages/back/index.jsp
member.login.success为用户登录成功,欢迎光临!
3 相关属性文件定义
消息属性文件
vo.add.success={0}\u6570\u636E\u589E\u52A0\u6210\u529F\uFF01
vo.add.failure={0}\u6570\u636E\u589E\u52A0\u5931\u8D25\uFF01
vo.edit.success={0}\u6570\u636E\u4FEE\u6539\u6210\u529F\uFF01
vo.edit.failure={0}\u6570\u636E\u4FEE\u6539\u5931\u8D25\uFF01
vo.rm.success={0}\u6570\u636E\u5220\u9664\u6210\u529F\uFF01
vo.rm.failure={0}\u6570\u636E\u5220\u9664\u5931\u8D25\uFF01
member.login.success=\u7528\u6237\u767B\u5F55\u6210\u529F\uFF0C\u6B22\u8FCE\u5149\u4E34\uFF01
member.login.failure=\u767B\u5F55\u5931\u8D25\uFF0C\u9519\u8BEF\u7684\u7528\u6237\u540D\u6216\u5BC6\u7801\uFF01
member.logout.success=\u7528\u6237\u6CE8\u9500\u6210\u529F\uFF0C\u518D\u89C1\uFF01
member.password.edit.success=\u7528\u6237\u5BC6\u7801\u4FEE\u6539\u6210\u529F\uFF0C\u4E3A\u4E86\u4FDD\u8BC1\u5B89\u5168\u8BF7\u91CD\u65B0\u767B\u5F55\uFF01
member.password.edit.failure=\u7528\u6237\u5BC6\u7801\u4FEE\u6539\u5931\u8D25\uFF0C\u4E3A\u4E86\u4FDD\u8BC1\u5B89\u5168\u8BF7\u91CD\u65B0\u767B\u5F55\uFF01
member.edit.password.admin.success=\u7528\u6237\u5BC6\u7801\u91CD\u7F6E\u6210\u529F\uFF01
member.edit.password.admin.failure=\u7528\u6237\u5BC6\u7801\u91CD\u7F6E\u5931\u8D25\uFF01
task.over.success=\u4EFB\u52A1\u5173\u95ED\u6210\u529F\uFF01
task.over.failure=\u4EFB\u52A1\u5173\u95ED\u6210\u529F\uFF01
task.finish.success=\u4EFB\u52A1\u6210\u529F\u5B8C\u6210\uFF01
task.finish.failure=\u4EFB\u52A1\u66F4\u65B0\u51FA\u9519\uFF0C\u672A\u5B8C\u6210\uFF01页面属性文件
error.page=/pages/errors.jsp
forward.page=/pages/forward.jsp
#back.emp.add.jsp=/pages/emp/emp_add.jsp
#back.emp.list.jsp=/pages/emp/emp_list.jsp
member.login.page=/login.jsp
member.logout.servlet=/LoginServletBack/logout
index.page=/pages/back/index.jsp
default.page=/pages/back/default.jsp
member.password.edit.page=/pages/back/member/member_edit_password.jsp
client.add.page=/pages/back/client/client_add.jsp
client.add.servlet=/pages/back/client/ClientServletBack/addPre
client.list.page=/pages/back/client/client_list.jsp
client.list.servlet=/pages/back/client/ClientServletBack/listSplit
client.edit.page=/pages/back/client/client_edit.jsp
mclient.list.page=/pages/back/mclient/client_list.jsp
mclient.show.page=/pages/back/mclient/client_show.jsp
mclient.list.servlet=/pages/back/mclient/ManagerClientServletBack/listSplit
mtask.list.page=/pages/back/mtask/task_list.jsp
mtask.list.servlet=/pages/back/mtask/ManagerTaskServletBack/listSplit
mtask.show.page=/pages/back/mtask/task_show.jsp
mtask.client.list.page=/pages/back/mtask/task_client_list.jsp
task.add.page=/pages/back/task/task_add.jsp
task.add.servlet=/pages/back/task/TaskServletBack/addPre
task.client.list.page=/pages/back/task/task_client_list.jsp
task.show.page=/pages/back/task/task_show.jsp
task.list.page=/pages/back/task/task_list.jsp
task.list.servlet=/pages/back/task/TaskServletBack/listSplit
task.edit.page=/pages/back/task/task_edit.jsp
news.add.page=/pages/back/news/news_add.jsp
news.add.servlet=/pages/back/news/NewsServletBack/addPre
news.list.page=/pages/back/news/news_list.jsp
news.list.servlet=/pages/back/news/NewsServletBack/list
news.show.page=/pages/back/news/news_show.jsp
news.edit.page=/pages/back/news/news_edit.jsp
action.list.page=/pages/back/action/action_list.jsp
groups.list.page=/pages/back/groups/groups_list.jsp
groups.show.page=/pages/back/groups/groups_show.jsp
role.add.page=/pages/back/role/role_add.jsp
role.add.servlet=/pages/back/role/RoleServletBack/addPre
role.list.page=/pages/back/role/role_list.jsp
role.list.servlet=/pages/back/role/RoleServletBack/list
role.show.page=/pages/back/role/role_show.jsp
role.edit.page=/pages/back/role/role_edit.jsp
member.add.page=/pages/back/member/member_add.jsp
member.add.servlet=/pages/back/member/MemberServletBack/addPre
member.list.page=/pages/back/member/member_list.jsp
member.list.servlet=/pages/back/member/MemberServletBack/listSplit
member.edit.page=/pages/back/member/member_edit.jsp
member.edit.password.admin.servlet=/pages/back/member/MemberServletBack/editPassPre
member.edit.password.admin.page=/pages/back/member/member_edit_password_admin.jsp4 登录后内容区显示内容,通过DefaultServletBack的show函数实现。
@SuppressWarnings("serial")
@WebServlet("/pages/back/DefaultServletBack/*")
public class DefaultServletBack extends AbstractCRMServlet {
public String show() {
try {
Map<String, Object> map = ServiceFactory
.getIDefaultServiceBackInstance().stat(super.getMid());
super.request.setAttribute("allNewses", map.get("allNewses"));
super.request.setAttribute("allClients", map.get("allClients"));
super.request.setAttribute("allTasks", map.get("allTasks"));
super.request.setAttribute("clientCount", map.get("clientCount"));
super.request.setAttribute("unfinishCount",
map.get("unfinishCount"));
super.request.setAttribute("wfinishCount", map.get("wfinishCount"));
} catch (Exception e) {
e.printStackTrace();
}
return "default.page";
}
......
}五 业务层模块设计实现
1 业务层登录代码实现
public class MemberServiceBackImpl extends AbstractCRMServiceBack implements
IMemberServiceBack {
@Override
public Map<String, Object> login(Member vo) throws Exception {
Map<String, Object> result = new HashMap<String, Object>();
boolean flag = false;
try {
// 1、进行用户名和密码的验证,如果登录成功将返回flag、rid两个字段的数据
if (DAOFactory.getIMemberDAOInstance(this.dbc.getConnection())
.findLogin(vo)) {
// 2、更新member表中的最后一次登录日期时间
if (DAOFactory.getIMemberDAOInstance(this.dbc.getConnection())
.doUpdateLastdate(vo.getMid())) {
result.put(
"unread",
DAOFactory.getIMemberNewsDAOInstance(
this.dbc.getConnection())
.getAllCountUnread(vo.getMid()));
// 3、在登录日志表中进行一条数据的保存
Logs logs = new Logs();
logs.getMember().setMid(vo.getMid());
if (DAOFactory
.getILogsDAOInstance(this.dbc.getConnection())
.doCreate(logs)) {
// 4、根据用户的角色编号,查询出用户对应的权限组信息
List<Groups> allGroups = DAOFactory
.getIGroupsDAOInstance(this.dbc.getConnection())
.findAllByRole(vo.getRole().getRid());
Set<Integer> gids = new HashSet<Integer>();
// 5、根据每一个权限组的编号,查询出对应的所有权限信息
Iterator<Groups> iter = allGroups.iterator();
// 6、根据每一个权限组的编号查询出所有的权限信息
while (iter.hasNext()) {
Groups gup = iter.next();
gids.add(gup.getGid());
gup.setAction(DAOFactory.getIActionDAOInstance(
this.dbc.getConnection()).findAllByGroups(
gup.getGid()));
}
// 7、将权限组的信息保存在角色里面
vo.getRole().setGroups(allGroups);
flag = true;
result.put(
"allActions",
DAOFactory.getIActionDAOInstance(
this.dbc.getConnection())
.findAllByGroups(gids));
}
}
}
} catch (Exception e) {
throw e;
} finally {
this.dbc.close();
}
result.put("flag", flag);
return result;
}
......
}六 数据层模块设计实现
1 登录逻辑数据层实现在MemberDAOImpl寻找,还有一些实现需要在其他地方寻找。
package cn.mldn.crm.dao.impl;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import cn.mldn.crm.dao.IMemberDAO;
import cn.mldn.crm.dao.abs.AbstractDAOImpl;
import cn.mldn.crm.vo.Member;
public class MemberDAOImpl extends AbstractDAOImpl implements IMemberDAO {
public MemberDAOImpl(Connection conn) {
super(conn);
}
@Override
public boolean doCreate(Member vo) throws Exception {
String sql = "INSERT INTO member(mid,rid,password,tel,photo,locked,flag) VALUES (?,?,?,?,?,?,?)" ;
super.pstmt = super.conn.prepareStatement(sql) ;
super.pstmt.setString(1, vo.getMid());
super.pstmt.setInt(2, vo.getRole().getRid());
super.pstmt.setString(3, vo.getPassword());
super.pstmt.setString(4,vo.getTel());
super.pstmt.setString(5, vo.getPhoto());
super.pstmt.setInt(6, vo.getLocked());
super.pstmt.setInt(7, vo.getFlag());
return super.pstmt.executeUpdate() > 0 ;
}
@Override
public boolean doUpdate(Member vo) throws Exception {
String sql = "UPDATE member SET rid=?,tel=?,locked=?,photo=? WHERE mid=?" ;
super.pstmt = super.conn.prepareStatement(sql) ;
super.pstmt.setInt(1, vo.getRole().getRid());
super.pstmt.setString(2, vo.getTel());
super.pstmt.setInt(3, vo.getLocked());
super.pstmt.setString(4, vo.getPhoto());
super.pstmt.setString(5, vo.getMid());
return super.pstmt.executeUpdate() > 0 ;
}
@Override
public boolean doRemove(Set<String> ids) throws Exception {
StringBuffer buf = new StringBuffer();
buf.append("DELETE FROM ").append("member").append(" WHERE ")
.append("mid").append(" IN ( ");
Iterator<String> iter = ids.iterator();
while (iter.hasNext()) {
buf.append("'").append(iter.next()).append("'").append(",");
}
buf.delete(buf.length() - 1, buf.length()).append(")");
buf.append(" AND flag=0") ; // 只能够删除普通用户
this.pstmt = this.conn.prepareStatement(buf.toString());
return this.pstmt.executeUpdate() == ids.size();
}
@Override
public List<Member> findAll() throws Exception {
// TODO Auto-generated method stub
return null;
}
@Override
public Member findById(String id) throws Exception {
Member vo = null ;
String sql = "SELECT mid,rid,tel,lastdate,photo,flag,locked FROM member WHERE mid=?" ;
super.pstmt = super.conn.prepareStatement(sql) ;
super.pstmt.setString(1, id);
ResultSet rs = super.pstmt.executeQuery() ;
if (rs.next()) {
vo = new Member() ;
vo.setMid(rs.getString(1));
vo.getRole().setRid(rs.getInt(2));
vo.setTel(rs.getString(3));
vo.setLastdate(rs.getDate(4));
vo.setPhoto(rs.getString(5));
vo.setFlag(rs.getInt(6));
vo.setLocked(rs.getInt(7));
}
return vo;
}
@Override
public List<Member> findAllSplit(String column, String keyWord,
Integer currentPage, Integer lineSize) throws Exception {
List<Member> all = new ArrayList<Member>() ;
String sql = "SELECT mid,rid,tel,lastdate,photo,flag,locked FROM member WHERE " + column + " LIKE ? LIMIT ?,?" ;
super.pstmt = super.conn.prepareStatement(sql) ;
super.pstmt.setString(1, "%" + keyWord + "%");
super.pstmt.setInt(2, (currentPage - 1) * lineSize);
super.pstmt.setInt(3, lineSize);
ResultSet rs = super.pstmt.executeQuery() ;
while (rs.next()) {
Member vo = new Member() ;
vo.setMid(rs.getString(1));
vo.getRole().setRid(rs.getInt(2));
vo.setTel(rs.getString(3));
vo.setLastdate(rs.getDate(4));
vo.setPhoto(rs.getString(5));
vo.setFlag(rs.getInt(6));
vo.setLocked(rs.getInt(7));
all.add(vo) ;
}
return all;
}
@Override
public Integer getAllCount(String column, String keyWord) throws Exception {
return super.handleCount("member", column, keyWord);
}
// 查表获得用户的flag和rid,并写入到vo返回,供业务层使用
@Override
public boolean findLogin(Member vo) throws Exception {
String sql = "SELECT flag,rid FROM member WHERE mid=? AND password=? AND locked=0" ;
super.pstmt = super.conn.prepareStatement(sql) ;
super.pstmt.setString(1, vo.getMid());
super.pstmt.setString(2, vo.getPassword());
ResultSet rs = super.pstmt.executeQuery() ;
if (rs.next()) {
vo.setFlag(rs.getInt(1));
vo.getRole().setRid(rs.getInt(2));
return true ;
}
return false;
}
@Override
public boolean doUpdateLastdate(String id) throws Exception {
String sql = "UPDATE member SET lastdate=? WHERE mid=?" ;
super.pstmt = super.conn.prepareStatement(sql) ;
System.out.println(new Date().getTime());
super.pstmt.setTimestamp(1, new Timestamp(new Date().getTime()));
super.pstmt.setString(2, id);
return super.pstmt.executeUpdate() > 0;
}
@Override
public boolean doUpdatePassword(String mid, String password)
throws Exception {
String sql = "UPDATE member SET password=? WHERE mid=?" ;
super.pstmt = super.conn.prepareStatement(sql) ;
super.pstmt.setString(1, password);
super.pstmt.setString(2, mid);
return super.pstmt.executeUpdate() > 0 ;
}
}七 参考