项目
博客
文档
归档
资源链接
关于我
项目
博客
文档
归档
资源链接
关于我
18| 构建实体及分类结构& 用户信息查询和地址模块功能(结构分层规范)+越权攻击
2024-08-08
·
·
原创
·
·
本文共 1,088个字,预计阅读需要 4分钟。
### 实体对象分类及结构 数据库实体对象(Entity):对应数据库中的表。使用单数名词命名,并且通常以 `Entity` 后缀结尾。 业务传输层实体对象(DTO ): Data Transfer Object,用于表示层(Controller)与业务逻辑层(Service)之间的数据传输。包含业务逻辑层需要的所有字段,这些字段可能不直接映射到数据库中的表结构。 数据传输对象(DO ):Data Object,用于应用程序内部的任何层之间的数据传输,通用的数据传输对象,不特定于某一层或业务场景。 请求响应实体对象(Request/Response):请求响应实体对象用于API的请求和响应。它们通常包含HTTP请求或响应所需的所有字段。 值对象(VO):Value Object,一种设计模式,表示一个没有标识符的简单数据结构,其主要目的是封装数据和行为。特点:**数据封装**,**数据传输**,**不变性**。 项目分层结构 ```java |-- model |-- entity // 数据库实体对象 |-- UserEntity.java |-- DO // 数据传输对象 |-- OrderDO.java |-- VO // 值对象,返回实体对象 |-- UserRegistrationVO |-- request/ // 请求实体对象 |-- UserRegistrationRequest.java ``` ### 用户信息查看(根据Token解析获取用户Id) 结构分层:`mapper,model,DO,service,vo,controller` `mapper查询返回的是定义的DO对象,在service处理之后转换为VO对象对外。可通过 BeanUtils.copyProperties(DO, VO);转换`。 定义VO: `@JsonProperty`用于对外字段命名不一样的转换 ```JAVA @Data public class UserVO { private Long id; /** * 昵称 */ private String name; /** * 头像 */ @JsonProperty("head_img") private String headImg; /** * 用户签名 */ private String slogan; /** * 0表示女,1表示男 */ private Integer sex; /** * 积分 */ private Integer points; /** * 邮箱 */ private String mail; } ``` controller层添加用户查询: ```java /** * 用户个人信息查询 */ @ApiOperation("个人信息查询") @GetMapping("detail") public JsonData detail(){ return JsonData.buildSuccess(userService.findUserDetail()); } ``` service增加接口及业务实现: - `通话获取拦截器中threadLocal解析的用户信息`。 - `BeanUtils.copyProperties(Entity, VO)将Entity对象转换为VO对象` ```java @Override public UserVO findUserDetail() { LoginUser loginUser = LoginInterceptor.threadLocal.get(); UserEntity user = userMapper.selectOne(new QueryWrapper
().eq("id", loginUser.getId())); UserVO userVO = new UserVO(); BeanUtils.copyProperties(user, userVO); return userVO; } ``` ### 新增收货地址/根据id查看地址信息模块(默认收货地址) 定义地址Entity ```java @Data @EqualsAndHashCode(callSuper = false) @TableName("address") public class AddressEntity implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Long id; /** * 用户id */ private Long userId; /** * 是否默认收货地址:0-否;1-是 */ private Integer defaultStatus; /** * 收发货人姓名 */ private String receiveName; /** * 收货人电话 */ private String phone; /** * 省/直辖市 */ private String province; /** * 市 */ private String city; /** * 区 */ private String region; /** * 详细地址 */ private String detailAddress; private Date createTime; } ``` 定义新增收货地区的请求对象: ```java @Data @ApiModel(value = "地址对象", description = "新增收货地址对象") public class AddressAddRequest { /** * 是否默认收货地址:0->否;1->是 */ @ApiModelProperty(value = "是否是否默认收货地址,0->否;1->是", example = "0") @JsonProperty("default_status") private Integer defaultStatus; /** * 收发货人姓名 */ @ApiModelProperty(value = "收发货人姓名", example = "yuan-paas") @JsonProperty("receive_name") private String receiveName; /** * 收货人电话 */ @ApiModelProperty(value = "收货人电话", example = "11111111111") private String phone; /** * 省/直辖市 */ @ApiModelProperty(value = "省/直辖市", example = "四川省") private String province; /** * 市 */ @ApiModelProperty(value = "城市", example = "成都市") private String city; /** * 区 */ @ApiModelProperty(value = "区", example = "武侯区") private String region; /** * 详细地址 */ @ApiModelProperty(value = "详细地址", example = "天府新区18号") @JsonProperty("detail_address") private String detailAddress; } ``` controller层增加查询及新增收货地址 ```java @Api(tags = "收货地址控制器") @Slf4j @ResponseResult @RestController @RequestMapping("/user/address/v1") public class AddressController { @Resource private AddressService addressService; @ApiOperation("根据id查找地址详情") @GetMapping("/find/{address_id}") public Object detail( @ApiParam(value = "地址id", required = true) @PathVariable("address_id") long addressId) { return JsonData.buildSuccess(addressService.detail(addressId)); } @ApiOperation("新增收货地址") @PostMapping("add") public JsonData add(@ApiParam("地址对象") @RequestBody AddressAddRequest addressAddRequest) { addressService.add(addressAddRequest); return JsonData.buildSuccess(); } } ``` service层及实现: `新增收货地址默认配置`:新增地址,并设置为默认地址:查询是否存在默认地址,如果存在,将原来的默认地址修改为非默认,新增的地址为默认地址。是两个数据修改操作,要添加事务`@Transactional`。 使用查询: `Wrappers.
lambdaQuery().eq(AddressEntity::getId, id)` ```java @Slf4j @Service public class AddressServiceImpl extends ServiceImpl
implements AddressService { @Override public AddressEntity detail(Long id) { //new QueryWrapper
().eq("id", id) return baseMapper.selectOne(Wrappers.
lambdaQuery() .eq(AddressEntity::getId, id)); } @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED) @Override public void add(AddressAddRequest addressAddRequest) { LoginUser loginUser = LoginInterceptor.threadLocal.get(); AddressEntity address = new AddressEntity(); address.setCreateTime(new Date()); address.setUserId(loginUser.getId()); BeanUtils.copyProperties(addressAddRequest, address); //是否有默认收货地址 if (address.getDefaultStatus() == AddressStatusEnum.DEFAULT_STATUS.getStatus()) { //查找数据库是否有默认地址 AddressEntity defaultAddressDO = baseMapper.selectOne( Wrappers.
lambdaQuery() .eq(AddressEntity::getId, loginUser.getId()) .eq(AddressEntity::getDefaultStatus, AddressStatusEnum.DEFAULT_STATUS.getStatus())); if (defaultAddressDO != null) { //修改为非默认收货地址 defaultAddressDO.setDefaultStatus(AddressStatusEnum.COMMON_STATUS.getStatus()); baseMapper.update(defaultAddressDO, Wrappers.
lambdaQuery() .eq(AddressEntity::getId, defaultAddressDO.getId())); } } int rows = baseMapper.insert(address); log.info("新增收货地址:rows={},data={}", rows, address); } } ``` 收货地址状态枚举: ```java /** * 收货地址状态 */ public enum AddressStatusEnum { /** * 是默认收货地址 */ DEFAULT_STATUS(1), /** * 非默认收货地址 */ COMMON_STATUS(0); private final int status; AddressStatusEnum(int status) { this.status = status; } public int getStatus() { return status; } } ``` ### 查看用户全部收货地址/删除地址(错误码定义) 返回地址信息Vo对象 ```java /** * 收货地址返回对象 */ @Data public class AddressVO { private Long id; /** * 用户id */ private Long userId; /** * 是否默认收货地址:0->否;1->是 */ @JsonProperty("default_status") private Integer defaultStatus; /** * 收发货人姓名 */ @JsonProperty("receive_name") private String receiveName; /** * 收货人电话 */ private String phone; /** * 省/直辖市 */ private String province; /** * 市 */ private String city; /** * 区 */ private String region; /** * 详细地址 */ @JsonProperty("detail_address") private String detailAddress; } ``` controller层 ```java /** * 删除指定收货地址 */ @ApiOperation("删除指定收货地址") @DeleteMapping("/del/{address_id}") public JsonData del( @ApiParam(value = "地址id", required = true) @PathVariable("address_id") int addressId) { int rows = addressService.del(addressId); return rows == 1 ? JsonData.buildSuccess() : JsonData.buildResult(BizCodeEnum.ADDRESS_DEL_FAIL); } /** * 查询用户的全部收货地址 */ @ApiOperation("查询用户的全部收货地址") @GetMapping("/list") public JsonData findUserAllAddress() { return JsonData.buildSuccess(addressService.listUserAllAddress()); } ``` service层实现 ```java @Override public int del(int addressId) { return baseMapper.delete(Wrappers.
lambdaQuery().eq(AddressEntity::getId, addressId)); } @Override public List
listUserAllAddress() { LoginUser loginUser = LoginInterceptor.threadLocal.get(); List
list = baseMapper.selectList(Wrappers.
lambdaQuery().eq(AddressEntity::getUserId, loginUser.getId())); return list.stream().map(obj -> { AddressVO addressVO = new AddressVO(); BeanUtils.copyProperties(obj, addressVO); return addressVO; }).collect(Collectors.toList()); } ``` `错误码枚举BizCodeEnum定义`: ```java /** * 通用操作码 */ OPS_REPEAT(110001,"重复操作"), /** * 购物车 */ CART_FAIL(220001,"添加购物车失败"), /** *验证码 */ CODE_TO_ERROR(240001,"接收号码不合规"), CODE_LIMITED(240002,"验证码发送过快"), CODE_ERROR(240003,"验证码错误"), CODE_CAPTCHA_ERROR(240101,"图形验证码错误"), /** * 账号 */ ACCOUNT_REPEAT(250001,"账号已经存在"), ACCOUNT_UNREGISTER(250002,"账号不存在"), ACCOUNT_PWD_ERROR(250003,"账号或者密码错误"), ACCOUNT_UNLOGIN(250004,"账号未登录"), /** * 优惠券 */ COUPON_CONDITION_ERROR(270001,"优惠券条件错误"), COUPON_UNAVAILABLE(270002,"没有可用的优惠券"), COUPON_NO_EXITS(270003,"优惠券不存在"), COUPON_NO_STOCK(270005,"优惠券库存不足"), COUPON_OUT_OF_LIMIT(270006,"优惠券领取超过限制次数"), COUPON_OUT_OF_TIME(270407,"优惠券不在领取时间范围"), COUPON_GET_FAIL(270407,"优惠券领取失败"), COUPON_RECORD_LOCK_FAIL(270409,"优惠券锁定失败"), /** * 订单 */ ORDER_CONFIRM_COUPON_FAIL(280001,"创建订单-优惠券使用失败,不满足价格条件"), ORDER_CONFIRM_PRICE_FAIL(280002,"创建订单-验价失败"), ORDER_CONFIRM_LOCK_PRODUCT_FAIL(280003,"创建订单-商品库存不足锁定失败"), ORDER_CONFIRM_ADD_STOCK_TASK_FAIL(280004,"创建订单-新增商品库存锁定任务"), ORDER_CONFIRM_TOKEN_NOT_EXIST(280008,"订单令牌缺少"), ORDER_CONFIRM_TOKEN_EQUAL_FAIL(280009,"订单令牌不正确"), ORDER_CONFIRM_NOT_EXIST(280010,"订单不存在"), ORDER_CONFIRM_CART_ITEM_NOT_EXIST(280011,"购物车商品项不存在"), /** * 收货地址 */ ADDRESS_ADD_FAIL(290001,"新增收货地址失败"), ADDRESS_DEL_FAIL(290002,"删除收货地址失败"), ADDRESS_NO_EXITS(290003,"地址不存在"), /** * 支付 */ PAY_ORDER_FAIL(300001,"创建支付订单失败"), PAY_ORDER_CALLBACK_SIGN_FAIL(300002,"支付订单回调验证签失败"), PAY_ORDER_CALLBACK_NOT_SUCCESS(300003,"创建支付订单失败"), PAY_ORDER_NOT_EXIST(300005,"订单不存在"), PAY_ORDER_STATE_ERROR(300006,"订单状态不正常"), PAY_ORDER_PAY_TIMEOUT(300007,"订单支付超时"), /** * 流控操作 */ CONTROL_FLOW(500101,"限流控制"), CONTROL_DEGRADE(500201,"降级控制"), CONTROL_AUTH(500301,"认证控制"), /** * 文件相关 */ FILE_UPLOAD_USER_IMG_FAIL(600101,"用户头像文件上传失败"); ``` ### 数据安全问题-越权攻击 查看/删除/操作他人的数据。 #### 水平-垂直越权攻击和代码防范 越权攻击介绍是Web应用程序中⼀种常见的漏洞,由于其存在范围广、危害大, 列为Web应用十大安全隐患的第⼆名。`指应⽤在检查授权时存在纰漏,使得攻击者在获得低权限⽤户账户后,利⽤⼀些⽅式绕过权限检查,访问或者操作其他⽤户或者更⾼权限`. 产生原因:`主要是因为开发⼈员在对数据进⾏增、删、改、查询时对客户端请求的数据过分相信,⽽遗漏了权限的判定`。 `⽔平越权`:`攻击者尝试访问与他拥有相同权限的⽤户的资源`。例子:A用户可以直接操作到B用户的数据 `垂直越权` :`⼀个低级别攻击者尝试访问⾼级别⽤户的资源`。例子:普通管理员登录,拼接浏览器地址,直接访问高级管理员的页面。 **防范水平越权**:`建⽴⽤户和可操作资源的绑定关系`,用户对任何资源进行操作时,通过该绑定关系确保该资源是属于该用户所有的防范垂直越权 **防范垂直越权**:`基于RBAC⻆⾊访问控制机制来防⽌纵向越权攻击,定义不同的权限⻆⾊,为每个⻆⾊分配不同的权限`,当用户执行某个动作或产生某种行为时,通过用户所在的角色判定该动作或者行为是否允许。