代码量:7300行+
技术栈:JavaWeb + Spring + SpringMVC + MyBatis + Mysql + Maven + Git
1.服务端环境
1.jdk 2.vsftpd 3.nginx 4.mysql 5.tomcat 6.git 7.maven 8.Redis
采用tomcat集群+nginx负载均衡,原因如下
为了解决服务器负载过大,在有的时候,系统的并发量过大,一台服务器无法负担的起用户发送的请求,因此我们需要搭建服务器集群,而nginx负载均衡就是接收用户的请求将其转发给后台服务器集群上的每一台机器
2.用户管理模块
知识点:
-
横向越权、纵向越权安全漏洞
横向越权:攻击者想访问与他权限相同的用户
纵向越权:低级别攻击者想访问高级别用户的资源如何避免横向越权:以重置密码为例,当找回问题与问题答案匹配时,可以进行充值密码操作,如果此时被攻击者攻击,攻击者可以随意修改改用户的密码。所以在找回问题与问题答案匹配时,给用户一个token并向Guava缓存中存入,并设置有效期,在有效期内,用户想要修改密码时,需要从Guava缓存中拿取token并于用户的token比对,如果一致则证明是本人操作,可以进行修改
如何避免纵向越权:给每个用户设置他的角色,区分是普通用户还是管理员,在进行特殊操作时,进行身份判断,只有当角色是
-
MD5明文加密及增加salt值
MD5加密就是将密码(普通字符串)按照指定的函数变成一个复杂的序列。比如在注册时,将用户的密码进行MD5散列后存入数据库,登录时同样将用户输入的密码和数据库中的MD5密码进行比较。
如果不用MD5加密,那么后台管理者可以直接看到用户的密码,有恶意操作的风险。使用MD5后,就算管理者看到了MD5密码,但是依然无法登录,因为登录时需要输入MD5加密前的密码,而MD5密码是不可逆的,无法根据MD5的值推出原密码的值。增加盐值(salt):如果两个用户的密码一样,那么MD5的值也一样,那么一个用户就知道了另一个用户的密码了,增加盐值就是给MD5再增加随机数,这样每一个密码对应的MD5都是独一无二的
-
高复用服务响应对象的设计思想及抽象封装
package com.mmall.common; import org.codehaus.jackson.annotate.JsonIgnore; import org.codehaus.jackson.map.annotate.JsonSerialize; import java.io.Serializable; /** * @author 袁梦达 2019012364 */ @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) //保证序列化json的时候,如果是null的对象,key也会消失 public class ServerResponse<T> implements Serializable { private int status; private String msg; private T data; private ServerResponse(int status){ this.status = status; } private ServerResponse(int status, T data){ this.status = status; this.data = data; } private ServerResponse(int status, String msg, T data){ this.status = status; this.data = data; this.msg = msg; } private ServerResponse(int status, String msg){ this.status = status; this.msg = msg; } @JsonIgnore //使之不在json序列化结果当中 public boolean isSuccess(){ return this.status == ResponseCode.SUCCESS.getCode(); } public int getStatus() { return status; } public String getMsg() { return msg; } public T getData() { return data; } public static <T> ServerResponse<T> createBySuccess(){ return new ServerResponse<T>(ResponseCode.SUCCESS.getCode()); } public static <T> ServerResponse<T> createBySuccessMessage(String msg){ return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg); } public static <T> ServerResponse<T> createBySuccess(T data){ return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), data); } public static <T> ServerResponse<T> createBySuccess(String msg, T data){ return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(), msg, data); } public static <T> ServerResponse<T> createByError(){ return new ServerResponse<T>(ResponseCode.ERROR.getCode(), ResponseCode.ERROR.getDesc()); } public static <T> ServerResponse<T> createByErrorMessage(String errorMessage){ return new ServerResponse<T>(ResponseCode.ERROR.getCode(), errorMessage); } public static <T> ServerResponse<T> createByErrorCodeMessage(int errorCode, String errorMessage){ return new ServerResponse<T>(errorCode, errorMessage); } }
3.分类管理模块
知识点:
- 如何设计及封装无限层级的树状数据结构
- 递归算法的设计思想
4.商品模块
知识点:
-
FTP文件上传
流程:先将文件保存在tomcat服务器,然后通过FTP工具类将tomcat上的文件上传到FTP服务器上,最后将tomcat上的文件删除 -
SpringMVC文件上传
-
流读取Properties配置文件
package com.mmall.util; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStreamReader; import java.util.Properties; /** * @author 袁梦达 2019012364 */ public class PropertiesUtil { private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class); private static Properties props; static { String fileName = "mmall.properties"; props = new Properties(); try { props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName), "UTF-8")); } catch (IOException e) { logger.error("配置文件读取异常", e); } } public static String getProperty(String key){ String value = props.getProperty(key.trim()); if(StringUtils.isBlank(value)){ return null; } return value.trim(); } public static String getProperty(String key, String defaultValue){ String value = props.getProperty(key.trim()); if(StringUtils.isBlank(value)){ return defaultValue; } return value.trim(); } }
-
抽象POJO、BO、VO对象之间的转换关系及解决思路
-
joda-time的使用
package com.mmall.util; import org.apache.commons.lang3.StringUtils; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import java.util.Date; /** * @author 袁梦达 2019012364 */ public class DateTimeUtil { public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss"; public static Date strToDate(String dateTimeStr, String formatStr){ DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr); DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr); return dateTime.toDate(); } public static String dateToStr(Date date, String formatStr){ if(date == null){ return StringUtils.EMPTY; } DateTime dateTime = new DateTime(date); return dateTime.toString(formatStr); } public static Date strToDate(String dateTimeStr){ DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT); DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr); return dateTime.toDate(); } public static String dateToStr(Date date){ if(date == null){ return StringUtils.EMPTY; } DateTime dateTime = new DateTime(date); return dateTime.toString(STANDARD_FORMAT); } }
5.购物车模块
知识点:
- 如何封装一个高复用购物车核心方法
- Bigdecimal解决浮点型商业运算中丢失精度的问题
因为用普通的数据类型进行运算时,会产生误差,后果很严重。而且BigDecimal的参数要传入字符串,而不是int,double,不然还是会产生误差package com.mmall.util; import java.math.BigDecimal; /** * @author 袁梦达 2019012364 */ public class BigDecimalUtil { private BigDecimalUtil() { } public static BigDecimal add(double v1, double v2){ BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2); } public static BigDecimal sub(double v1, double v2){ BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2); } public static BigDecimal mul(double v1, double v2){ BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2); } public static BigDecimal div(double v1, double v2){ BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP); //四舍五入,保留2位小数 } }
6.收货地址模块
知识点:
- SpringMVC数据绑定中对象绑定
- mybatis自动生成主键、配置和使用
- 如何避免横向越权漏洞的巩固
比如每个人只能增删改查自己的收货地址,所以在进行增删改查前,先判断该userId和Shipping是不是一致
7.支付模块
知识点:
- 支付宝对接核心文档,调通支付宝支付功能官方Demo
- 解析支付宝SDK对接源码
- RSA1和RSA2验证签名及加解密
- 避免支付宝重复通知和数据校验
- 生成二维码,并持久化到图片服务器
- 外网穿透
8.订单模块
知识点:
- 设计实用、安全、扩展性强大的常量、枚举类
- mybatis批量插入