开发中遇到的问题和解决方案
项目技术栈
前台(面向用户):
前端:Vue 2、webpack、Element UI、axios, 后端:Spring Boot、Mybatis-plus、Lombok,Spring Security、JWT 数据库:MySQL、Redis 客服聊天功能:Websocket
后台管理系统:
前端:thymeleaf、Jquery,
后端:Spring Boot、Mybatis、Shiro、MySQL、Swagger,
文件存储:阿里云OSS对象存储
部署:Docker
登录状态的保持以及登录状态过期的处理
- 登录状态的保持
由于我们项目的架构是前后端分离,使用传统的session认证会存在很大的弊端。如由于session保存在服务端,随着用户的增多,服务器开销会增大,且会在分布式系统中失效,且session认证无法跨域,对单点登录不适用。对于非浏览器的客户端、手机移动端等不适用,因为session
依赖于cookie
,而移动端经常没有cookie
解决方案:
我们的登录功能是通过Spring Security + JWT(Json Web Token),用户在登陆后,在Redis中缓存已登陆的用户信息,客户端中保存服务端签发的token,每次前端请求后端的需要权限的接口都在Http请求头中携带token,后端Spring Security会根据token获取用户的权限信息,判断用户是否有权限获取该信息。
- 登录过期的处理
我们在保存在redis中的用户信息和token是有过期时间的,假如用户要退出登录,那么用户必须携带token请求后端退出登录的接口,删除redis中用户的登录信息,如果此时token或redis中的LoginUser过期了,Spring Security无法获取到用户的信息,就会报错,从而影响到前端的效果展示。
解决方案:1.redis不设置过期时间并在token快过期时刷新token,不采用,如果用户长期不访问网站,会占用redis资源。2.要实现退出登录,则自定义退出处理类,实现LogoutSuccessHandler,这样就不会在退出登录时,出现权限校验失败的情况。
前端提示用户状态过期,跳转用户登录页面请重新登录
密码的安全问题
首先,我们保存在数据库中的密码都是经过spring security加密的密文,但是在前端提交登录信息到后端的密码时明文,在http协议中,攻击者很容易在密码传输过程中进行劫持拿到你的密码
解决方案:
- 使用HTTPS协议加密传输数据,可以有效防止HTTP劫持,我们打算在部署后采用。
- 使用RSA非对称加密算法密码,使用支付宝开发平台助手生成公钥和私钥,将公钥存在前端(前端的所有代码对用户都是可见的),私钥存在后端,在注册或登录过程中使用公钥对密码加密,到后端使用私钥进行解密,最后在后端再加密保存在数据库或与数据库密文进行matches匹配校验用户密码是否正确。这样一定程度上保证了用户密码再传输过程中的安全性。
权限控制
1.用户再未登录时,不能访问客服、创建项目、我的项目。登录后不能再访问登录页面。
解决方案:Vue的路由守卫实现权限控制
2.用户由两种:普通用户和客服,二者在登录后看到的页面组件应该不同,如客服左侧应该是主页和用户列表
解决方案:要保证普通用户不会通过特殊手段跳转到客服的页面,用户在登录时后端校验用户权限,返回在数据库中对应权限路由组件的相关信息如组件名、路由地址等,前端Vue通过路由守卫和路由导航实现权限控制
OSS文件上传的性能问题
参考文档:阿里云OSS最佳实践(https://help.aliyun.com/document_detail/31926.html)
Web端常见的上传方法是用户在浏览器或App端上传文件到应用服务器,应用服务器再把文件上传到OSS。具体流程如下图所示。
存在以下缺点:
- 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS,网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
- 扩展性差:如果后续用户数量逐渐增加,则应用服务器会成为瓶颈。
- 费用高:需要准备多台应用服务器。由于OSS上行流量是免费的,如果数据直传到OSS,将节省多台应用服务器的费用。
解决方案:
服务端签名直传:
客服功能(网页聊天室)的实现
参考资料:黑马程序员:WebSocket打造在线聊天室(https://www.bilibili.com/video/BV1r54y1D72U)
传统方法:使用Http协议实现;
弊端:HTTP协议是一种单向、无连接、无状态的应用层协议。通信请求只能由客户端发起,服务端应答,服务端无法主动向客户端发起消息,这种单向请求的特点,注定了如果服务端有连续的状态变化,客户端要获知就非常困难。大多数web应用程序将通过频繁的异步Ajax请求实现长轮询。
轮询是指浏览器通过JavaScript启动一个定时器,然后以固定的间隔给服务器发请求,询问服务器有没有新消息。这个机制的缺点一是实时性不够,二是频繁的请求会给服务器带来极大的压力轮询的效率低,非常浪费资源。
解决方案
使用websocket协议。使用websocket建立连接,减少系统性能开销