1、在整个项目中,我们采用的是nginx+tomcat来部署的(面试官可能会问nginx是谁来部署的?如何部署的?nginx的执行流程、优点),nginx一方面做加载静态资源的服务器,另一方面来做反向代理和负载均衡。因为该项目需要在多个环境中运行,我们利用了nginx的反向代理解决了不同环境同系统访问地址不统一带来的问题。
2、因为整个项目实现的功能较多,所以采用分布式的架构设计,整个项目包括后台管理系统、商城首页系统、搜索系统、商品详情页系统、登录系统、购物车系统、订单系统等,这样做的好处是使每个功能模块独立出来,降低了各系统之间的耦合度,增删一个功能不会影响其他功能模块。
整个项目采用分布式的架构设计,包括登录系统、搜索系统、购物车系统、订单系统、支付系统等。整个项目采用nginx+tomcat来部署,nginx主要用来做反向代理和负载均衡。主要用redis来做登录信息缓存,mysql做数据库。自己参与了登录系统的开发,包括注册、单点登录等功能模块。
- 单点登录是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
在redis中,用户可以通过执行SLAVEOF命令或者设置slaveof选项,让一个服务器去复制(replicate)另一个服务器,我们称呼被复制的服务器为主服务器(master),而对主服务器进行复制的服务器则被称为从服务器(slave)
使用键盘钩子拦截键盘输入内容,防止被其他工具记录,类似银行网银安全输入控件。
为了保证每次加密的结果的不同(防止跨域提交或截取加密信息伪提交),每次加密的key的一部分由服务器端随机生成,在页面加载的时候有服务器端生成通过页面js脚本传递给密码输入控件,密码输入控件根据控件内置的密码和传入的密码进行加密(如DES),服务器端接受后再进行解密得到原密码。
使用密码插件可以有效防止密码及管件信息在网络上被截取,导致账户密码丢失,保证服务器至用户端的安全传输。
之前实现的登录和注册是在同一个tomcat内部完成,而现在系统架构是每一个系统都是由一个团队进行维护,每个系统都是单独部署运行一个单独的tomcat,所以,不能将用户的登录信息保存到session中(多个tomcat的session是不能共享的,即session共享问题),所以我们需要一个单独的系统来维护用户的登录信息。
我们是这样做之后,用户去登录页面登录,发送含有用户信息的请求且会携带cookie去服务端数据库查询是否有该用户,如果没有提示用户,如果有就把用户信息保存到redis中,并生成一个token保存到cookie中。
注: 当前方式就是搭建共享的Session服务器
当用户点击登录按钮的时候,用户输入用户名和密码,检验用户名是否在数据库中存在,然后用户名密码是否正确。这里的密码是用了spring的MD5加密技术。当全部成功后,将sessionId(也可以生成一个UUID)写入cookie中供前端调用,写入浏览器的cookie中,然后存入到redis中(key是sessionId,value是用户信息),并设置有效期。
这里的cookie是设置了共同的name,所以不论是什么系统进行登录,前端页面都会存有这个name的cookie,也就实现了所有子系统都可以访问到cookie。
当用户登录其他子系统时,先从cookie中获取token信息(也就是sessionId),根据token信息获取用户信息。用户每次与网站的交互,比如查看产品,则刷新一次redis的时间,重新设置有效期,这个效果是通过拦截器来实现的。
拦截器的拦截,在springMVC.xml中设置拦截的名称。
登录流程代码:先写cookie再写redis.
拦截器重置有效期:
web.xml部署拦截器:
1、登录页面提交用户名密码。
2、登录成功后生成token。Token相当于原来的jsessionid,字符串,可以使用uuid。
3、把用户信息保存到redis。Key就是token,value就是TbUser对象转换成json。
4、使用String类型保存Session信息。可以使用“前缀:token”为key
5、设置key的过期时间。模拟Session的过期时间。一般半个小时。
6、把token写入cookie中。
1.从cookie中取token
2.取不到未登录
3.取到token,到redis中查询token是否过期
4.如果过期,为登录状态
5.没有过期,登录状态
将cookie存在一个公共的站点的页面上就可以了,这里我们管那个站点叫主站S。
环境1:a.xxx.com需要跟b.xxx.com实现跨域,这种比较简单,只需要设置cookie的域名关联域就可以了 cookie.Domain = "xxx.com",这样两个域名间的cookie就可以互相访问,实现跨域。【项目中使用的这种环境】
环境2:a.aaa.com需要跟b.bbb.com实现跨域,这种不同域名的情况下,想要实现就必须换种方式.
在这里我将引入第三者,s.sss.com这个站点,就是某个浏览器同时打开了这3个站点,我们访问A站点,先判断自身是否登录,如果session为空,就重定向到S站点,判断S站点上面是否有cookie,如果S站点上面也没有cookie,则由S站点重定向到A站点的登录页.
这样我们就实现了第一步,S站做的的就是隐藏在幕后,子站先判断自己是否存在session,如果不存在,就重定向到主站S上面去验证.
第二步,验证登录信息合法性.这里我引入token(令牌),网上有很多资料,描述token的传递,工作方式是这样,A登录成功,保存自身的session,重定向到S,S在自己站点保存一个session跟cookie,session保存token对象{tokenID,userName,startTime,endTime},cookie保存tokenID,tokenID是一个Guid,把token对象缓存在集合里面,另起一个线程,根据endTime(过期时间)来定期清理集合列表,重定向到A的时候再将tokenID传递过去,拿到tokenID后,进入验证环节,S站有提供一个接口,根据tokenID获取token对象,如果获取到对象,且没有失效,则tokenID合法,跳入index页面.情况2,A登录,直接打开B,这时候B自身没有session,会主动请求主站,主站会返回cookieID(S站存在客户端的cookie),这个时候再走验证环节,如果通过,则B根据token对象创建自身的session,再跳入index。
- 去登陆页面
- 提交登陆页面
- 用户名、密码、验证码的校验
- 错误信息的回显
- 保存用户到Session中
- 重定向到登陆之前的访问页面
- Ajax跨域判断用户是否登陆
主要实现商品管理和商品规格参数管理,对商品和商品规格进行CRUD操作。在实现前台调用后台数据时,为了实现系统间的调用,便使用了HttpClient技术来实现此功能,在后台提供了需要调用的接口。(HttpClient介绍、工作原理、优缺点)。如果在后台对商品进行操作,为了使前台数据与后台数据实现同步,我们使用了ActiveMQ消息队列机制实现商品同步功能(ActiveMQ介绍、工作原理、优缺点)。
我们考虑了会员在未登录和登录两种情况下把商品加入购物车,后台如何该保存商品信息。
在用户商品详情页点击加入购物车的时候,我们用了登录拦截器来判断用户是否登录。如果没有登录,将商品信息保存到cookie中,当用户登录后,再把商品持久到数据库中;但是考虑到cookie储存大小(4k)的问题,还有当cookie储存的数据越多就会影响响应速度,我们决定使用redis来缓存用户在未登录状态下的商品信息(redis介绍、原理、优缺点),在redis中设置缓存生存时间(如何做到的?),如果用户在规定时间内没有登录,数据便会自动删除。如果用户在规定时间内登录了,便会通过ActiveMQ消息队列机制将数据同步到数据库中。
1、要求用户登录。
2、把购物车商品列表保存到数据库中。推荐使用redis。
3、Key:用户id,value:购车商品列表。推荐使用hash,hash的field:商品id,value:商品信息。
4、在用户未登录情况下写cookie。当用户登录后,访问购物车列表时,
a)把cookie中的数据同步到redis。
b)把cookie中的数据删除
c)展示购物车列表时以redis为准。
d)如果redis中有数据cookie中也有数据,需要做数据合并。相同商品数量相加,不同商品添加一个新商品。
5、如果用户登录状态,展示购物车列表以redis为准。如果未登录,以cookie为准。
购物车放99件,同一家拍货最多50件不同物品一单,多出重新拍。主要是为了防止部分买家无限制囤货以及保障网站的正常运行,以避免对购物车对买家以及网站运行造成不利影响。
项目中首页的大广告和商品类目这些不需要经常修改的数据,如果用户每次刷新页面的时候都要去数据库中查询,这样会浪费资源和增加数据库的压力。
像解决思路:
所以我们想当把这些数据添加到一个缓存中,用户去访问的时候,先去缓存中命中,如果命中失败,再去数据库中查询,然后把查询到的数据添加到缓存中。
具体方案:
目前比较主流的缓存技术有Redis和Memcached,单纯从缓存命中的角度来说,Memcached要高一些,可Redis和Memcache的差距其实并不大,但Redis提供的功能更加强大一些,读写速度也很快。所以我们选用了Redis来缓存数据。
redis把数据以key—value的形式缓存到内存中,并提供了多种数据存储类型(String、Hash、list、Set、SortedSet),还自身提供了持久化功能(2种:RDB、AOF),还可以把数据备份到磁盘中(redis的SAVE命令用于创建当前 redis数据库的备份),防止redis宕机时的数据丢失。(会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步)。我们使用的是spring与redis的java的客户端jedis进行整合,可以利用jedis做分片式集群,解决了redis内存受限的问题。
在后台管理系统中采用了Maven的多模块化的管理,其中采用了水平切分的方式(垂直与水平划分的区别:垂直:功能模块明确,层次不够清晰,代码重用性差。水平:层次清晰,代码重用性高,易于独立维护。),将各层分层开发,这样做的好处是代码重用性高,层次清晰,易于独立维护。系统内部接口调用采用Httpclient(Dubbo),接口提供端采用RESTful风格的接口定义(一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件)
系统之间的通知机制采用MQ的方式,使用ActiveMQ的实现,使用了ActiveMQ的消息订阅模式的消息机;
采用了nginx+tomcat的模式,其中nginx的作用一方面是做反向代理、负载均衡、另一方面是做图片等静态资源的服务器。
使用网易云信提供的短信发送功能。
- 快捷支付 —— 银行卡支付
- 支付宝余额付款 —— 支付宝的余额支付
- 网点付款 —— 线下合作网点(商店等)付款
- 消费卡付款 —— 手机卡完成付款
- 找人代付 —— 购物后,指定别人完成网上付款
因为项目是采用分布式架构设计的,各模块之间是相互独立的,而各模块的访问路径又是不同的,所以当跨域请求数据的时候会遇到跨域受限的问题。比如当用户首次访问该网站首页时,首页页面会异步请求后台管理系统加载商品的类目,这是就会出现跨域受限的问题,以前开发时,如果在本模块内,我们是通过ajax异步请求数据的,但ajax不支持跨域,所以用ajax无法解决跨域请求数据的问题。
跨域是指从一个域名的网页去请求另一个域名的资源。浏览器出于安全的考虑,不允许不同源的请求
JSONP解决AJAX跨域问题:
JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个