假如我们需要做个后台管理模块,我们来模拟下登陆过程
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毫秒
结束:这个小例子就到这里结束