在《由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。变化真的挺大的。但是千变万变,代码的“心”是不变的。颇有一种“我看世事多变幻,世事看我却依然”的感觉。
项目的备份策略:
项目的迭代策略:
项目的前端框架:
未完待续