关于一个小项目

       在《由Javaweb所想到的》这篇文章里已经说了很多的理论知识。现在是时候做一个小小的项目,来对这些理论做一次检验了。

       首先说明一下,我要做的这个“小项目”,并不是一个真正的项目,它乃是一个赤裸裸的http服务集合。也就是说我不会去写前端。所有的一切我都会通过postman这个软件,或者直接通过浏览器的地址栏去访问测试这些http服务。

     当然这个项目不会太复杂,http服务不会太多。我也不想一开始就直接列出http的各种服务,因为我感觉这也太枯燥乏味了一点。这个工程首先需要的是一个大的轮廓。那么它就是一个简单的博客系统。各位博客网站放心好了。只不过是个简单的练习。肯定不是觊觎博客这块蛋糕,不过话说这块蛋糕当年真的还是蛮大的啊。当然了在这个讲究团队合作的年代,一个人想在一个行业里有所建树那已经很难了。最起码博主我已经不存一丁点幻想了,所以纯粹就是练习而已了。

       嗯,首先我先建了一个文件夹,取名叫做数据库。事实上,这件事情很简单,比我写这么多的字要简单多了。但是说实话,我不想去做。为什么?因为文字我们人类已经用了几千年了。而文件夹我们只用了几十年。所以这就叫做惯性?懂吗?惯性。。。。。。

      所以要我说实话的话,我宁愿用文字去写上十万八千个字也不愿意动手去建一个文件夹。“建一个文件夹“说实话,这句话真的有点怪怪的。

      可惜啊,本博主实在天资有限,不能靠卖字真乃一大遗憾。只能靠编程为生。虽说编程这家伙古板刻薄,真的没有文学精灵古怪那么可爱。但是有一大好处,那就是编程这家伙它不会动,就像那手里的玩具一样,想玩便玩,不想玩放一边即可。此话怎讲?就是说这家伙跟砖头差不多,搬来倒去从来不变。对于这种不变的家伙,那在人类面前不就是个板凳吗?

       这么看来,编程也有可爱的地方啊。

      所以本博主就不得不委曲求全,勉为其难地开始动工了。

      然后填一张表

项目名称:简单博客

项目http服务简单介绍:这个也不用一个个介绍,概括到位即可。

发送验证码 参数:手机号

登录   参数:手机号 验证码

查看别人的文章列表 参数:无

查看别人的文章  参数:文章id

查看自己的文章  参数:文章id

发表文章 参数:文章题目 文章正文

修改文章 参数:文章id

对文章进行打分 参数:文章id 参数

查看自己的文章列表  参数:无

删除文章 参数:文章id

因为名字叫做简单博客,所以功能做的也很简单,总共也就10个http访问。

特点分析:主要使用自增主键id进行文章识别

项目的数据库结构:这个有辅助软件,使用E-R图即可。填写参考什么什么E-R图即可。

因为是java编程 所以不妨先想一想需要哪些承载数据的pojo类吧。

文章类 字段是 id 文章名 文章正文 分数 用户id

用户类 字段是 id 手机号码

用户不设置用户名,所以每个人都是匿名的。

也许需要做哪些http服务,基本上已经决定了数据库基本包含哪些数据了。

项目的用户认证方式:

使用token认证,在第一次用户登录的时候,给用户发送token,以后每次用户访问可以不用登录,只要带着此token进行访问就行。token失效后仍然需要重新登录才行。

首先是token的生成,有时候token的生成需要传一个独特的参数。

其实这个参数本身也是能够作为一个token的。因为这个参数本来就是独一无二的。

那么为什么不直接使用它呢?

那是因为这个参数虽然独特,但是它有一个致命的缺点,就是没有保密性。因为token要具备两个特点,一个就是独特性,另一个就是保密性。所以token其实就是相当于 是用户名和密码的二合一的这么一个东西。而且验证token的时候都是在内存发生的,跟数据库那是一点儿关系都没有。而验证用户名和密码的时候往往要通过查询数据库来验证的。那么有人说了,把所有的用户名和密码都放内存中,验证的时候不是也和数据库没有关系了吗?这样子也是可以的。但是这就需要解决一个问题,即内存与数据库数据同步的问题。所以综合来看token的方式性能上还是要好一些。

       那么token是如何产生的呢?有没有具体的代码呢?这个网上是有的,而且还多的是。

       比如:

/**  

 * 生成Token的工具类:  

 */  

package red.hearing.eval.modules.token;  


import java.security.MessageDigest;  

import java.security.NoSuchAlgorithmException;  

import java.util.Random;  


import sun.misc.BASE64Encoder;  


/**  

 * 生成Token的工具类  

 * @author zhous  

 * @since 2018-2-23 13:59:27  

 *  

 */  

public class TokenProccessor {  


     private TokenProccessor(){};  

private static final TokenProccessorinstance =new TokenProccessor();  


    public static TokenProccessor getInstance() {  

        return instance;  

    }  


    /**  

     * 生成Token  

     * @return  

     */  

    public String makeToken() {  

Stringtoken = (System.currentTimeMillis() + new Random().nextInt(999999999)) + "";  

         try {  

MessageDigestmd =MessageDigest.getInstance("md5");  

            byte md5[] =  md.digest(token.getBytes());  

BASE64Encoderencoder =new BASE64Encoder();  

            return encoder.encode(md5);  

        } catch (NoSuchAlgorithmException e) {  

            // TODO Auto-generated catch block  

            e.printStackTrace();  

        }  

         return null;  

    }  

代码出处:http://www.mamicode.com/info-detail-2347408.html?__cf_chl_jschl_tk__=5187b4cbae1ce3f798484c0ee3a1cb33215a22dd-1590133965-0-ATNIrRMRioittH1p_Z638x7nrCtoffpYUDzRMZA2twEJdn592NfR_B50ArAOfIxVxoBpH28XZeU9ilqEgIyLy3yYVVHmlmIMfAcLBStOAomDTJYUJQkB_jexv6F_x6DdXTVsrgQbYDXbmmrpUWq0_BW-1A7R_gyWFOYQUxleBTld2RsMs9idiv0-8GuYcg88BlMKptgMlGBUXuc-OEdLhUyPVVMmksTihpslziPcp8njeLJulgh5SK_xQ58phGonMYaFIhPWyS_4o5fRpL_M9JBu4Z2rVmEWqz6p7c21j0ItOUgxKxTFPjApfGtcNcxjCA

比如这个程序中生成的token,是一段“杂乱无章”的字符串。那么这样的token 除非是被黑客拦截。要不然以现在的技术手段几乎是“猜”不出来的。如果说普通人设置的密码是普通的锁,那么这个字符串可以说是保险柜上的锁了。

那么当生成token之后,就需要传给客户端了。如何传给客户端,token是可以放在cookie中的。不管是不是放在cookie中,一般都是放在http的header中的。而与客户端相关的信息,一般都放在session中。所以或许将token放置到session中就可以了。而这只是一种猜想,具体还要看网上一些代码。于是我从网上找来了代码。

/**  

 *   

 */  

package red.hearing.eval.modules.token;  


import javax.servlet.http.HttpServletRequest;  


import org.apache.commons.lang3.StringUtils;  


/**  

 * Token的工具类  

 * @author zhous  

 * @since 2018-2-23 14:01:41  

 *  

 */  

public class TokenTools {  


    /**  

     * 生成token放入session  

     * @param request  

     * @param tokenServerkey  

     */  

    public static void createToken(HttpServletRequest request,String tokenServerkey){  

Stringtoken =TokenProccessor.getInstance().makeToken();  

        request.getSession().setAttribute(tokenServerkey, token);  

    }  


    /**  

     * 移除token  

     * @param request  

     * @param tokenServerkey  

     */  

    public static void removeToken(HttpServletRequest request,String tokenServerkey){  

        request.getSession().removeAttribute(tokenServerkey);  

    }  


    /**  

     * 判断请求参数中的token是否和session中一致  

     * @param request  

     * @param tokenClientkey  

     * @param tokenServerkey  

     * @return  

     */  

    public static boolean judgeTokenIsEqual(HttpServletRequest request,String tokenClientkey,String tokenServerkey){  

Stringtoken_client =request.getParameter(tokenClientkey);  

        if(StringUtils.isEmpty(token_client)){  

            return false;  

        }  

Stringtoken_server = (String) request.getSession().getAttribute(tokenServerkey);  

        if(StringUtils.isEmpty(token_server)){  

            return false;  

        }  


        if(!token_server.equals(token_client)){  

            return false;  

        }  


        return true;  

    }  



代码出处:http://www.mamicode.com/info-detail-2347408.html?__cf_chl_jschl_tk__=5187b4cbae1ce3f798484c0ee3a1cb33215a22dd-1590133965-0-ATNIrRMRioittH1p_Z638x7nrCtoffpYUDzRMZA2twEJdn592NfR_B50ArAOfIxVxoBpH28XZeU9ilqEgIyLy3yYVVHmlmIMfAcLBStOAomDTJYUJQkB_jexv6F_x6DdXTVsrgQbYDXbmmrpUWq0_BW-1A7R_gyWFOYQUxleBTld2RsMs9idiv0-8GuYcg88BlMKptgMlGBUXuc-OEdLhUyPVVMmksTihpslziPcp8njeLJulgh5SK_xQ58phGonMYaFIhPWyS_4o5fRpL_M9JBu4Z2rVmEWqz6p7c21j0ItOUgxKxTFPjApfGtcNcxjCA


在使用servlet的时候。可以直接操作session 从而返回数据就可以了。使用springmvc的时候,也是如此,

是的,是时候展示我从网上找的代码了,代码如下:

/**

* 读取所有cookie 

* 注意二、从客户端读取Cookie时,包括maxAge在内的其他属性都是不可读的,也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期 

* @param request 

* @param response 

*/@RequestMapping("/showCookies")publicvoidshowCookies(HttpServletRequestrequest,HttpServletResponseresponse){Cookie[]cookies=request.getCookies();//这样便可以获取一个cookie数组  if(null==cookies){System.out.println("没有cookie=========");}else{for(Cookiecookie:cookies){System.out.println("name:"+cookie.getName()+",value:"+cookie.getValue());}}}/** 

* 添加cookie 

* @param response 

* @param name 

* @param value 

*/@RequestMapping("/addCookie")publicvoidaddCookie(HttpServletResponseresponse,Stringname,Stringvalue){Cookiecookie=newCookie(name.trim(),value.trim());cookie.setMaxAge(30*60);// 设置为30min  cookie.setPath("/");System.out.println("已添加===============");response.addCookie(cookie);}/** 

* 修改cookie 

* @param request 

* @param response 

* @param name 

* @param value 

* 注意一、修改、删除Cookie时,新建的Cookie除value、maxAge之外的所有属性,例如name、path、domain等,都要与原Cookie完全一样。否则,浏览器将视为两个不同的Cookie不予覆盖,导致修改、删除失败。 

*/@RequestMapping("/editCookie")publicvoideditCookie(HttpServletRequestrequest,HttpServletResponseresponse,Stringname,Stringvalue){Cookie[]cookies=request.getCookies();if(null==cookies){System.out.println("没有cookie==============");}else{for(Cookiecookie:cookies){if(cookie.getName().equals(name)){System.out.println("原值为:"+cookie.getValue());cookie.setValue(value);cookie.setPath("/");cookie.setMaxAge(30*60);// 设置为30min  System.out.println("被修改的cookie名字为:"+cookie.getName()+",新值为:"+cookie.getValue());response.addCookie(cookie);break;}}}}/** 

* 删除cookie 

* @param request 

* @param response 

* @param name 

*/@RequestMapping("/delCookie")publicvoiddelCookie(HttpServletRequestrequest,HttpServletResponseresponse,Stringname){Cookie[]cookies=request.getCookies();if(null==cookies){System.out.println("没有cookie==============");}else{for(Cookiecookie:cookies){if(cookie.getName().equals(name)){cookie.setValue(null);cookie.setMaxAge(0);// 立即销毁cookie  cookie.setPath("/");System.out.println("被删除的cookie名字为:"+cookie.getName());response.addCookie(cookie);break;}}}}

//通常开发时先用以下的代码将获取的cookie进行封装

/**

    * 根据名字获取cookie 

    * @param request 

    * @param name cookie名字 

    * @return 

    */publicCookiegetCookieByName(HttpServletRequestrequest,Stringname){Map<String,Cookie>cookieMap=ReadCookieMap(request);if(cookieMap.containsKey(name)){Cookiecookie=(Cookie)cookieMap.get(name);returncookie;}else{returnnull;}}/** 

    * 将cookie封装到Map里面 

    * @param request 

    * @return 

    */privateMap<String,Cookie>ReadCookieMap(HttpServletRequestrequest){Map<String,Cookie>cookieMap=newHashMap<String,Cookie>();Cookie[]cookies=request.getCookies();if(null!=cookies){for(Cookiecookie:cookies){cookieMap.put(cookie.getName(),cookie);}}returncookieMap;}

代码出处:https://www.jianshu.com/p/a44b5781aa46

但是以上前两段代码好像并不符合我的需求。session的存在与客户端浏览器是否关闭有关系。那么是否说明session就是只适合于浏览器上的应用呢?如果是安卓应用,那是不是要使用另外一种内存呢?

那么问题出现了,那就是生成的token应该存放在哪里?。。。。。。其实这很可能是一个错误问题。。。。。。那是因为可能服务器端根本就不存储token。

从网上看了很多token的相关资料,感觉各个做法差别很大。大概就像是装修房子一样,几万块钱也能装修,十几万也能装修,而几十万几百万也是装修。或许没有绝对的安全。或许要具体情况具体来分析。如果只是作为少数人不涉及到金钱的一个应用。如果没有那么大的厉害关系,可能简单使用一下就可以。如果涉及到大的厉害关系。那么就应当注意一下了。

      下面使用真名讲一个小故事。

      http 在提供http服务的时候。是需要识别客户端的。如果是这么一种情况,http服务器发送一根管子给http客户端。然后这个管子永远坏不了。并且别的人无法撕开这根管子。如果是这种模型,那么只需要在管子连好之后,服务器对客户端识别一次即可。因为识别之后,服务器记住这根管子就行了。

       可是现实是这样子的。管子太贵了,也太少了。管子只能租不能买。

即使你这个服务器和客户端之间安好了这么一根管子。服务器和客户端通完信之后,就得把管子给拆了,然后归还。那么有人说了,我不好容易租一回管子,那我就让服务器和客户端多说几句话可以吗?说它个三天三夜。设计者早就想好了这种情况。所以这种情况是不被允许的。因为管子宝贵啊。所以公司规定了,安好管子之后。必须客户端先说话,而且只能说一句话,而且这句话不能超过10个字。客户端说完之后,服务器端说。服务器也是只能说一句话,而且这句话不能超过20个字。服务器说完之后,就得马上归还管子。再想说话,再租管子就是了。

       虽然管子很贵,但是客户端有钱啊,客户端于是就说了:“服务器,没事,咱两个尽情地聊,不要担心租管子的事情,我已经办了年卡,这一年可以不限次数随便租管子的”。然后客户端就和服务器愉快自由自在地聊起天了。服务器一见客户端这么热情,顿时非常感动。其实客户端是有很多的,而服务器其实只有一个。服务器亲眼见了各种各样的客户端。有的客户端一辈子只来找它聊一句或者几句,便在也不见这个客户端的影子了。有的客户端则可能天天来找它聊天,一天聊好几万句也是有的。但是更多的是那种偶尔来一次,或者隔几天来一次然后聊几句的那种客户端。不管怎么样,客户端每天过来聊天的次数,那可真是无法预测的。不过整体来说还算是均衡的。就像大马路上行人的密度一样,没有办法精确估计未来某一个时间的密度。但是总体来说行人的密度一般是可以估计在一个范围之内的,当然发生极端的情况下例外。而且上面所说的行人密度,其实还符合一个函数曲线的,就是那个著名的概率曲线。 

        不论这个曲线是什么样子的,总而言之每个客户端来聊天的次数是不一样的。由于管子是通用的,而且在计算机世界中,又没有所谓的个性,所以事实上所有的客户端几乎是一模一样的。所以每次安好管子的时候。服务器端都要根据客户端说出的话进行判断,那么它是怎么判断的呢?其实它是用一个小本子来解决这个问题的。

        从公司那里借来管子的时候,管子上印有客户端的一些信息,比如上网号码啊等等。而服务器端把这些都记在了这个本子上。当客户端说话的时候,服务器端根据客户端说的话里带的一个暗号 就从自己那本子上找,服务器端找啊找,     很快它就根据那暗号在那个本子上找到了那个暗号所对应的信息,原来上网号是XXX。于是服务器端知道了自己已经已经和这个上网号在半个小时内聊了50句了。所以这分明就是一个典型的“好客户端”啊。于是服务器热情地答话了。服务器就是这样,假如它从它那个小本上,查出此客户端以前从来没有来聊过天的话。那么它就会冷冰冰地说:“对不起客户端,你还没告诉我您叫什么名字,密码是多少” 本来客户端就想知道今天的天气,没想到得到这样的一个回答。所以客户端很沮丧,也没办法只能再和服务器通一次信。先告诉服务器自己的用户名密码再说。

       这种搞个小本子的做法也是可以的。但是这个小本子上的内容太容易被擦掉了,而且小本子也是很贵的啊。

于是服务器开始想别的主意了。“最近客户端不给钱了啊,所以小本子还是少买吧。”服务器想。于是服务器突然灵机一动想到:“那么干脆,让客户端多发点暗号过来吧,我直接看暗号得了。”所以从此以后这个服务器经常受到客户端很多暗号,它一看暗号就知道这个客户端的一些情况了。比如是不是第一次来聊天的等。

       当然了暗号是发过来了,服务器也得有眼力劲去看呢,其实服务器虽然没钱买本子,但是眼力劲还是有的。

它收到了暗号,马上就开始琢磨了,这暗号这么眼熟,肯定是我亲自制作的啊。当然只看一眼也是不行的,它把暗号的包装层层打开,于是“图穷而匕首现”打开之后它说:“没错这就是 最原来的东西啊”

       那么为什么要把匕首包在图里面呢?在历史书中这个问题可能比较简单。那么在整个过程中,哪些需要被包在地图里面呢?首先,图是不少的,但是不是每张图里面都有一个匕首。其次匕首放在图里面,可能有几个原因,一个是要验证算法。一个是为了保密匕首。   那么问题来了,匕首为什么要保密?用户在登录的时候给服务器的用户名密码显然都没有保密(意为加密)啊。那么怎么服务器给用户发的这个就需要保密了呢?其实很简单,因为它这个保密之后才算是密码,不保密之前那叫名字。所以即使从对等的角度来说,客户端给服务器发送的是密码,那么服务器给客户端也应该发密码才对。即使这密码是他下次还要查看的。

        所以也可以说,多给客户端发的暗号,就是随机生成的密码。然后发给了客户端。所以这里的加密的作用可能就是生成一个密码。    不过这密码是服务器为客户端定制的密码。

项目使用的中间件:比如springboot shiro springcloud tomcat等

那么以前说过,为了降低javaweb开发的难度,很多第三方公司开发出了一些中间件。

这些中间件就像一个保姆一样,帮助程序员从吃饭到洗衣服。。。总而言之,是能管的都管了。

接下来就看程序员的了。。。

那么这些“保姆”们到底都管了哪些事情呢?

比如,springboot都管了哪些事情呢?

现在开发一个javaweb,用到的“保姆”太多了。那么springboot这个“保姆”,她呢其实就是就是一个专门介绍别的“保姆”来工作的一个“保姆”。有了这样一个“保姆”,再来请其他“保姆”来干活那就方便的多了。不用自己一个一个麻烦地去找了。而且这个“保姆”找来的“保姆”都是非常可靠的。

所以这个项目会先请这个springboot这个“保姆”

然后在让这个“保姆”去请springmvc,mybatis,shiro,redis,rabbitqp这些“保姆”。

而这些“保姆”在系统上的安装,一般来说都是比较简单的。因为就像一款合格的应用软件一样。安装一般并没有太多需要注意的事情。只要运行成功了,基本就是正确的。


项目的开发周期:

项目的开发大致计划以及预备计划:

项目的开发工具:

其实本博主一直有个疑问没有想通。那就是为什么已经有Eclipse还有人要制造出Ideal。也许是为了赚钱吧。

总而言之,作为编辑软件,从txt到word最后又到Eclipse然后又到Ideal。变化真的挺大的。但是千变万变,代码的“心”是不变的。颇有一种“我看世事多变幻,世事看我却依然”的感觉。

项目的备份策略:

项目的迭代策略:

项目的前端框架:

未完待续

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,922评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,591评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,546评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,467评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,553评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,580评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,588评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,334评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,780评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,092评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,270评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,925评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,573评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,194评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,437评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,154评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,127评论 2 352