假如我们需要做个后台管理模块,我们来模拟下登陆过程
1、创建工程,准备好jar包,我使用的是mysql(这里提一句,mysql-jdbc的jar包官网下载是msi文件,解压就行了)
主要的jar包有下面这些:
2、创建工程,项目分为前台门户,和后台管理,webroot 下建 MenHu和HouTai两个文件夹,也可以把脚本,样式的文件夹都建好
如下图:
3、我们暂时只关注HouTai这一块
1)我们要的是什么
(一)、访问 localhost:8080/Programs/HouTai/时,进入后台登陆页面login.html
(二)、访问 .../HouTai/main(主页面)时,判断用户是否登陆了,如果没有登陆,那么进入login.html,如果登陆了就到main.html
(三)、登陆过程我们需要验证输入账号密码格式,成功后在验证密码是否正确。最后进入main.html
针对第一步,我们建数据表,添加几条数据进去,要有个用户表users,有id,name,password,phone几个字段就好了,这里我们使用phone 来做账号。
2)创建路由类baseRoute,三个控制器类IndexController和HouTaiController、CommonController(暂时控制了登陆和验证码)
baseRoute:
/**
* 内容摘要 :
*
* 类修改者 修改日期
* 这个基础路由,将门户网站和后台管理模块分开,分别由IndexController
* 和HouTaiController来控制页面路径。其他的action 的控制另外配置
* @ClassName IndexRoute
* <p>Company: knowology </p>
* @author c_wolf your emai address
* @date 2014-7-2 下午12:32:34
* @version V1.0
*/
public class baseRoute extends Routes{
@Override
public void config() {
//门户前端控制
add("/", IndexController.class,"/MenHu");
//后台管理模块控制
add("/HouTai",HouTaiController.class,"/HouTai");
//某些公共资源,例如验证码
add("Common",CommonController.class);
}
}
IndexC....:
public class IndexController extends Controller{
public void index(){
/**
* 一些逻辑,一些数据
*/
render("index.html");
}
public void about(){
render("about.html");
}
public void product(){
render("product.html");
}
}
HouTai...:
public class HouTaiController extends Controller {
//默认页面为登陆页面
public void index(){
render("login.html");
}
//跳转到后台管理页面主页面,默认先加载当前用户能操作的业务
public void main(){
User user = (User)getSessionAttr("user");
String id = user.get("id").toString();
String sql = "SELECT s.name,s.url,s.id FROM rel_user_service rel,service s WHERE rel.serviceid=s.id AND rel.userid=?";
List<Service> services = Service.dao.find(sql, id);
setAttr("services", services);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
render("main.html");
}
}
common..:
public class CommonController extends Controller {
public static final String KEY= "mykey";
@SuppressWarnings("rawtypes")
public Map responseM = new HashMap();//用来存放返回的数据(json)
//生成验证码
public void imgcode() {
//这个是jfinal自带的验证码生成类,可以直接使用的
CaptchaRender img = new CaptchaRender(KEY);
render(img);
}
@SuppressWarnings("unchecked")
@Before({LoginFormat.class,LoginPwd.class})//先进行格式验证,在验证密码正确性
//登陆action
public void login(){
//经过前面的验证,已经确定用户登录成功。
//查询用户基本信息,放入session(基本信息主要为,用户名,角色,id等,具体看个人的情况,想放什么就放什么)
String sql = "select id,username,phone from users where phone=? limit 1";
User user = User.dao.findFirst(sql, getPara("phone"));
setSessionAttr("user", user);
responseM.put("state", "success");
//返回成功登录的标志
renderJson(responseM);
}
DemoConfig:
@Override
public void configRoute(Routes me) {
me.add(new baseRoute());//加入我们的路由
}
通过这几个类,可以看出访问 /HouTai/main时,没有经过验证就进入了main.html。我们加一个handler 类,来控制下
public class BaseHandler extends Handler {
@Override
public void handle(String target, HttpServletRequest request,
HttpServletResponse response, boolean[] isHandled) {
//如果路径中包含HouTai(该路径下所有资源),那么验证身份,通过验证才能进入后台管理模块
if(target.indexOf("HouTai")>0){
HttpSession session = request.getSession(false);
if(session==null||session.getAttribute("user")==null){
//验证不成功,跳转到后台登陆页面
target="/HouTai/index";
}
}
nextHandler.handle(target, request, response, isHandled);
}
好了,这下用户进不去main.html了。我们来看下登陆页面
login.html:(登陆页面使用jquery 来异步验证登陆,成功后跳转页面,失败后,给提示)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>login.html</title>
<base href="/项目名/"><!--这里加上base个人觉得是有好处的,让页面引用资源全部基于项目根路径,不容易出错 -->
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<script src="Scripts/jquery-1.9.1.min.js"></script>
<link rel="shortcut icon" href="Images/favicon.ico" />
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
<script type="text/javascript">
$(function() {
$("#login_btn").removeAttr("disabled");
/**
*1、input绑定回车
*/
$("#login_form input").bind("keyup", function(e) {
var ev = document.all ? window.event : e;
if (ev.keyCode == 13) {//如果是回车,那么提交表单
submitForm();
}
});
});
/**
提交表单
*/
function submitForm() {
//清空所有错误提示
$(".msg").html("");
$("#login_btn").attr("disabled","disabled");
$.post("Common/login", $("#login_form").serialize(), function(data) {
$("#login_btn").removeAttr("disabled");
var responseM = data;
if(responseM.state=='error'){//登陆不成功,显示所有的错误提示
for(var key in responseM){
if(key!='error')
$("#"+key).html(responseM[key]);
}
}else{//登陆成功,跳转页面
location = "HouTai/main";
}
}, "json");
}
</script>
<style>
.msg{
color:red;
font-size:0.8em;
}
</style>
<body>
<!--这里说明下,form表单中,input 的name要和后面错误提示的id想对应,这样程序写起来会更方便 -->
<form id="login_form" method="post" action="Common/login">
电话:<input name='phone' /><span class='msg' id="phoneMsg"></span><br> <br>
密码:<input
name='password' type="password" /><span class='msg' id="passwordMsg"></span> <br> <br>
验证:<input name="imgcode" /><span class='msg' id='imgcodeMsg'></span><br> <br> <img
src="Common/imgcode"
onclick="this.src='Common/imgcode?'+Math.random()" /> <input
id="login_btn" onclick="submitForm()" type="button" value="登陆">
</form>
</body>
</html>
效果将会是这样的:
3)后台需要验证数据的格式,jfinal 中有validator。我们也建一个
public class LoginFormat extends Validator{
@SuppressWarnings("rawtypes")
public Map responseM = new HashMap();//用来存放返回的数据(json)
//进行phone
static String regPhone = "^1[1-9]{10}$";
//进行密码验证正则,6-15位数字和字母特殊字符的组合
static String regPassword="^[A-Za-z0-9(!@#$%&)]{6,15}$";
//验证码格式
static String regImgcode = "^[A-Za-z0-9(!@#$%&)]{6}";
@Override
protected void validate(Controller c) {
//验证账号和密码
validateRegex("phone", regPhone, "phoneMsg", "电话号码格式不正确!");
validateRegex("password", regPassword, "passwordMsg", "密码格式为6-15为英文与数字结合!");
validateRegex("imgcode", regImgcode, "imgcodeMsg", "验证码格式不正确!");
}
@SuppressWarnings("unchecked")
@Override
protected void handleError(Controller c) {
Enumeration<String> en =c.getParaNames();
while (en.hasMoreElements()) {
//错误键,我们规定,所有的错误为 请求字段加上 Msg
String key = en.nextElement().toString()+"Msg";
if(c.getAttrForStr(key)!=null){
responseM.put(key, c.getAttrForStr(key));
}
}
//这样我们可以将所有的错误作为一个json串返回前端页面
responseM.put("state", "error");
c.renderJson(responseM);
}
同时我们需要在之前的CommonController 的login方法上面加上 注解,在login 方法执行前,去验证数据格式
@Before(LoginFormat.class)
验证完格式后,我们还需要验证数据是否正确(验证码正确,密码正确),这时候需要用到数据库了,配置下:
@Override
public void configPlugin(Plugins me) {
//先加载配置文件,源码显示,加载的是web-inf下的文件,所以配置文件放在web-inf文件夹下
loadPropertyFile("jdbc.properties");
//getProperty
C3p0Plugin cp = new C3p0Plugin(getProperty("jdbcurl"),
"root", "root");
me.add(cp);
ActiveRecordPlugin arp = new ActiveRecordPlugin(cp);
me.add(arp);
arp.addMapping("users", User.class);
arp.addMapping("service", Service.class);
me.add(new EhCachePlugin());
}
验证数据的正确性,我们也创建个validator 来做,代码几乎类似(不要忘了login 方法加注解)
public class LoginPwd extends Validator {
private Map<String, String> responseM = new HashMap<String, String>();
@Override
protected void validate(Controller c) {
//验证码忽略大小写,这里用到了jfinal 自带的验证验证码的方法
boolean b = CaptchaRender.validate(c, c.getPara("imgcode").toUpperCase(), CommonController.KEY);
if(!b){
addError("imgcodeMsg", "验证码不正确!");
return;
}
//验证码正确,验证密码
String phone = c.getPara("phone");
//MD5加密后与数据库数据进行比较
String password = StringUtil.encodePassword(c.getPara("password"), "MD5");
String sql = "select password from users where phone =? limit 1";
User user = User.dao.findFirst(sql, phone);
if(!password.equals(user.get("password").toString())){
addError("passwordMsg", "密码不正确!");
return;
}
}
@Override
protected void handleError(Controller c) {
Enumeration<String> en =c.getParaNames();
while (en.hasMoreElements()) {
//错误键,我们规定,所有的错误为 请求字段加上 Msg
String key = en.nextElement().toString()+"Msg";
if(c.getAttrForStr(key)!=null){
responseM.put(key, c.getAttrForStr(key));
}
}
responseM.put("state", "error");
c.renderJson(responseM);
}
}
页面效果如下:
4)登陆成功后进入mian.html(我用了esayui,jquery,稍微加了几个div)
main.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>main.html</title>
<base href="/Pro/">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<link rel="shortcut icon" href="Images/favicon.ico" />
<script src="Scripts/jquery-1.9.1.min.js"></script>
<link rel="stylesheet" type="text/css"
href="Scripts/jquery-easyui-1.3.6/themes/icon.css">
<script type="text/javascript" src="Scripts/jquery.cookie.js"></script>
<script type="text/javascript" src="HouTai/HTScripts/common.js"></script>
<link type="text/css" rel="stylesheet" href="Styles/common.css" />
<body>
<div id="cc" class="easyui-layout" style="height:600;overflow:hidden;">
<div data-options="region:'north'" style="height:60px;border:none;background:url(Images/ht_bg.png)">
<div class="ht_top" style="folat:left;">
<div id='ht_logo_text'>
卖包子后台管理系统
</div>
<div style="float:right;color:white;margin-right:20px;">
<!-- 如果session 存在,那么就显示登陆成功,否则,跳转回到登陆页面 -->
<#if session??> 欢迎您:${session["user"]["username"]} <#else>
<script>
location = HouTai / index;
</script>
</#if>
<br>
<input name='n1' type="radio" onclick="changeTheme('default')">default
<input name='n1' type="radio" onclick="changeTheme('gray')">gray
<input name='n1' type="radio" onclick="changeTheme('bootstrap')">bootstrap
<input name='n1' type="radio" onclick="changeTheme('black')">black
<input name='n1' type="radio" onclick="changeTheme('metro')">metro
</div>
</div>
</div>
<div data-options="region:'center'" style="height:100px;">
<div class="easyui-tabs" style="height:450px">
<#list services as service>
<div title='${service["name"]}' style="padding:10px"></div>
</#list>
</div>
</div>
</div>
<br>
</body>
<script src="Scripts/jquery-easyui-1.3.6/jquery.easyui.min.js"></script>
</html>
最后显示效果为:(显示出当前欢迎你:登录人)
5)我的login方法中加了个sleep(500),模拟下后台加载很多数据。使用firebug 看到 main请求花了 505毫秒
我们给它加上缓存
缓存这个东西不是所有的方法都要加,看情况,例如一些大家都能用到的公共数据(门户首页数据)可以放缓存,让别人不用再加载了,私人数据直接放session就行了(类似私人缓存吧)
在login 方法上加上注解:
@Before(CacheInterceptor.class)
最后看下效果,main请求第二次加载只需要10毫秒
结束:这个小例子就到这里结束