赞
踩
简介:分布式文件存储解决核心知识介绍和面试题
数据爆炸的时代,产生的数据量不断地在攀升,基本都离不开文件存储
业务应用内存储
分布式文件系统(Distributed File System)
海量数据对存储提出了新的要求,从而诞生了分布式文件存储
是文件系统管理的物理存储资源不一定直接连接在本地节点上,而是通过计算机网络与节点(可简单的理解为一台计算机) 相连,或是若干不同的逻辑磁盘分区组合在一起而形成的完整的有层次的文件系统
自研:扩容容易-开发难
面试题:做分布式文件存储,主要是想实现文件存储访问的高性能与高可用,如何保证分布式存储的高性能与高可用?
大家可以想到基本就是副本备份、双活、多活这种架构
在系统中通过复制协议将数据同步到多个存储节点,并确保多个副本之间的数据一致性,当某个存储节点出故障时,系统能够自动将服务切换到其他的副本
在分布式存储中高性能与高可用是矛盾的,比如要设计一个分布式存储系统,CAP定理也可以推断出来
对性能的考虑,记录数据时先写一个份数据到某个机器上并立即返回,然后异步发起多个数据备份过程。这种设计的性能最好,但存在“容错性”的风险,加入返回后,还没来得及同步给其它节点就宕机了,则数据就丢失(异步复制,也存在是写主节点到内存还是落到磁盘)
如果同时写多个副本,每个副本写成功以后再返回,则又导致性能下降,这个过程取决于最慢的那台机器的性能 (同步多写,是同步每个副本节点还是一个副本先)
基于上述的,大家还知道有一个很类似的消息队列就是支持这种操作
RocketMQ消息高可用里面的
简介:分布式文件存储常见解决方案介绍
目前业界比较多这个解决方案,这边就挑选几个介绍下
是在 Apache License v2.0 下发布的对象存储服务器,学习成本低,安装运维简单,主流语言的客户端整合都有, 号称最强的对象存储文件服务器,且可以和容器化技术docker/k8s等结合,社区活跃但不够成熟,业界参考资料较少
官网:https://docs.min.io/cn/
一个开源的轻量级分布式文件系统,比较少的客户端可以整合,目前主要是C和java客户端,在一些互联网创业公司中有应用比较多,没有官方文档,社区不怎么活跃.
架构+部署结构复杂,出问题定位比较难定位,可以说是fastdfs零件的组装过程,需要去理解fastDFS的架构设计,才能够正确的安装部署
云厂商
阿里云OSS
七牛云
腾讯云
亚马逊云
CDN最强:Akamai https://www.akamai.com/cn
选云厂商理由
选开源MinIO的理由
简介:分布式文件存储MinIO容器化部署初体验
Docker容器化部署(用于测试体验)
docker run -p 9000:9000 \
--name minio_xdclass \
-v /Users/xdclass/Desktop/test:/data \
-e "MINIO_ROOT_USER=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
minio/minio server /data
步骤
总结
总体操作很流畅,支持单机和集群部署,多个方面都是目前比较强的,
对于有需求不能或不使用云厂商提供的存储服务,例如阿里云的oss、七牛云的对象存储等,可以通过自建minio对象存储集群的方式
简介:阿里云OSS对象存储介绍和开通
对象存储OSS(Object Storage Service)是阿里云提供的海量、安全、低成本、高持久的云存储服务。其数据设计持久性不低于99.9999999999%(12个9),服务设计可用性不低于99.995%。
OSS具有与平台无关的RESTful API接口,您可以在任何应用、任何时间、任何地点存储和访问任意类型的数据。
提供标准、低频访问、归档和冷归档四种存储类型,全面覆盖从热到冷的各种数据存储场景:
标准存储类型 | 高持久、高可用、高性能的对象存储服务,支持频繁的数据访问。是各种社交、分享类的图片、音视频应用、大型网站、大数据分析的合适选择。 |
---|---|
低频访问存储类型 | 适合长期保存不经常访问的数据(平均每月访问频率1到2次)。存储单价低于标准类型,适合各类移动应用、智能设备、企业数据的长期备份,支持实时数据访问。 |
归档存储类型 | 适合需要长期保存(建议半年以上)的归档数据,在存储周期内极少被访问,数据进入到可读取状态需要1分钟的解冻时间。适合需要长期保存的档案数据、医疗影像、科学资料、影视素材。 |
冷归档存储类型 | 适合需要超长时间存放的极冷数据。例如因合规要求需要长期留存的数据、大数据及人工智能领域长期积累的原始数据、影视行业长期留存的媒体资源、在线教育行业的归档视频等。 |
开通阿里云OSS
有阿里云账号、实名认证
OSS介绍:https://www.aliyun.com/product/oss
OSS控制台:https://oss.console.aliyun.com/bucket
学习路径:https://help.aliyun.com/learn/learningpath/oss.html
开通后的操作
简介:阿里云ram权限管理介绍
文件上传流程
云账号 AccessKey
RAM权限介绍
阿里云用于各个产品的权限,基于RBAC模型,进行简单管理账号、统一分配权限、集中管控资源,从而建立安全、完善的资源控制体系。
众多产品,一般采用子账号进行分配权限,防止越权
建立用户,勾选编程访问(保存accessKey和accessSecret,只出现一次)
简介:阿里云OSS对象存储客户端集成和测试服务
添加阿里云OSS的SDK
地址:https://help.aliyun.com/document_detail/32008.html
添加maven依赖
<!-- OSS各个项目单独加依赖,根据需要进行添加-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
<!-- OSS各个项目单独加依赖,根据需要进行添加-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
</dependency>
用户微服务配置OSS
#阿里云OSS配置
aliyun:
oss:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: LTAI4GKz7Z1RRjzMYeHG3FCe
access-key-secret: QilitkUsr0PMJYEK8MAPFdI4cFOLVq
bucketname: xd-test1
@ConfigurationProperties(prefix = "aliyun.oss")
@Configuration
@Data
public class OSSConfig {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketname;
}
简介:用户微服务上传用户头像到oss服务层开发
@Service @Slf4j public class FileServiceImpl implements FileService { @Autowired private OSSConfig ossConfig; @Override public String uploadUserHeadImg( MultipartFile file) { String originalFilename = file.getOriginalFilename(); //获取相关配置 String bucketName = ossConfig.getBucketname(); String endpoint = ossConfig.getEndpoint(); String accessKeyId = ossConfig.getAccessKeyId(); String accessKeySecret = ossConfig.getAccessKeySecret(); //创建oss对象 OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret); //JDK8新特性写法,构建路径 LocalDateTime ldt = LocalDateTime.now(); DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy/MM/dd"); String folder = dtf.format(ldt); String fileName = CommonUtil.generateUUID(); String extension = originalFilename.substring(originalFilename.lastIndexOf(".")); //在oss上创建文件夹test路径 String newFileName = "test/" + folder + "/" + fileName + extension; try { PutObjectResult result = ossClient.putObject(bucketName, newFileName, file.getInputStream()); //返回访问路径 if (null != result) { //https://xd-test1.oss-cn-beijing.aliyuncs.com/test/1.jpg String imgUrl = "https://"+bucketName+"."+endpoint+"/"+newFileName; return imgUrl; } } catch (Exception e) { log.error("上传头像失败:{}",e); } finally { // 关闭OSS服务 ossClient.shutdown(); } return null; } }
简介:用户微服务头像上传阿里云OSS接口和SwaggerUI提效
文件上传流程
注意:默认SpringBoot最大文件上传是1M,大家测试的时候记得关注下
开发controller
/** * 上传用户头像 * * 默认文件大小 1M,超过会报错 * * @param file * @return */ @ApiOperation("用户头像上传") @PostMapping(value = "upload") public JsonData uploadHeaderImg(@ApiParam(value = "文件上传",required = true) @RequestPart("file") MultipartFile file){ String result = fileService.uploadUserHeadImg(file); return result != null?JsonData.buildSuccess(result):JsonData.buildResult(BizCodeEnum.FILE_UPLOAD_USER_IMG_FAIL); }
Postman测试 (http的header是)
简介:用户微服务注册接口介绍和业务代码编写
微服务注册接口开发
简介:介绍信息安全,密码学的那些事情
加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,密文无法解密。
只有重新输入明文,并经过同样的加密算法处理,得到相同的密文并被系统重新识别后,才能真正解密
算法:MD5/SHA1/SHA224/SHA256/
优点:快速计算m,具有单向性 one-way,不可由散列值推出原消息
场景:文件完整性校验和(Checksum)算法、常规密码等
简介:用户微服务注册接口加密逻辑完善
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!--用于加密-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
/** * 生成指定长度随机字母和数字 * * @param length * @return */ private static final String ALL_CHAR_NUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; public static String getStringNumRandom(int length) { //生成随机数字和字母, Random random = new Random(); StringBuilder saltString = new StringBuilder(length); for (int i = 1; i <= length; ++i) { saltString.append(ALL_CHAR_NUM.charAt(random.nextInt(ALL_CHAR_NUM.length()))); } return saltString.toString(); }
UserDO userDO = new UserDO();
BeanUtils.copyProperties(registerRequest, userDO);
userDO.setCreateTime(new Date());
userDO.setSlogan("人生需要动态规划,学习需要贪心算法");
//生成秘钥
userDO.setSecret("$1$" + CommonUtil.getStringNumRandom(8));
//密码 + 加盐处理
String cryptPwd = Md5Crypt.md5Crypt(registerRequest.getPwd().getBytes(), userDO.getSecret());
userDO.setPwd(cryptPwd);
简介:高并发下账号唯一性安全保证方案
注册业务
高并发下问题发现扩大
Redis:先看redis是否有,然后没的话则是新的注册
数据库唯一索引(建表的时间已经添加)
ALTER TABLE user ADD unique(`mail`)
简介:用户微服务登录模块开发
/**
* 登录
* @param loginRequest
* @return
*/
@PostMapping("login")
public JsonData register(@RequestBody UserLoginRequest loginRequest){
JsonData jsonData = userService.login(loginRequest);
return jsonData;
}
/** * 登录 * * @param vo * @return */ @Override public JsonData login(UserLoginRequest loginRequest) { List<UserDO> list = userMapper.selectList( new QueryWrapper<UserDO>().eq("mail", loginRequest.getMail())); if (list != null && list.size() == 1) { UserDO userDO = list.get(0); String cryptPwd = Md5Crypt.md5Crypt(loginRequest.getPwd().getBytes(), userDO.getSecret()); if (cryptPwd.equals(userDO.getPwd())) { //生成token令牌 return JsonData.buildSuccess(); } //密码错误 return JsonData.buildResult(BizCodeEnum.ACCOUNT_PWD_ERROR); } else { //未注册 return JsonData.buildResult(BizCodeEnum.ACCOUNT_UNREGISTER); } }
简介:分布式应用的登录检验解决方案 JWT讲解 json web token
什么是JWT
{ id:888, name:'小D', expire:10000 } funtion 加密(object, appsecret){ xxxx return base64( token); } function 解密(token ,appsecret){ xxxx //成功返回true,失败返回false }
优点
缺点
token是经过base64编码,所以可以解码,因此token加密前的对象不应该包含敏感信息,如用户权限,密码等
如果没有服务端存储,则不能做登录失效处理,除非服务端改秘钥
JWT格式组成 头部、负载、签名
关于jwt客户端存储
讲解:引入相关依赖并开发JWT工具类, 开发生产token和校验token的办法
聚合工程加入版本依赖,common项目加入相关依赖
<!-- JWT相关 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
common项目中封装生产token方法
/** * 根据用户信息,生成令牌 * * @param user * @return */ public static String geneJsonWebToken(LoginUser user) { Long userId = user.getId(); String token = Jwts.builder().setSubject(SUBJECT) .claim("head_img", user.getHeadImg()) .claim("id", userId) .claim("name", user.getName()) .claim("mail", user.getMail()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + EXPIRE)) .signWith(SignatureAlgorithm.HS256, SECRET).compact(); token = TOKEN_PREFIX + token; return token; }
封装校验token方法
/** * 校验token的方法 * * @param token * @return */ public static Claims checkJWT(String token) { try { final Claims claims = Jwts.parser().setSigningKey(SECRET) .parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody(); return claims; } catch (Exception e) { return null; } }
登录整合
//生成token令牌
LoginUser userDTO = new LoginUser();
BeanUtils.copyProperties(userDO, userDTO);
String token = JWTUtil.geneJsonWebToken(userDTO);
return JsonData.buildSuccess(token);
讲解:JWT过期自动刷新方案介绍
在前后分离场景下,越来越多的项目使用jwt token作为接口的安全机制,但存在jwt过期后,用户无法直接感知,假如在用户操作页面期间,突然提示登录,则体验很不友好,所以就有了token自动刷新需求
但是这个自动刷新方案,基本都离不开服务端状态存储,JWT推出思想是:去中心化,无状态化,所以有所违背
类似这样的业务,有阿里云首页,没有做token刷新令牌维护,但是符合对应的思想
用户登录成功的时候,一次性给他两个Token,分别为AccessToken和RefreshToken AccessToken有效期较短,比如1天或者5天,用于正常请求 RefreshToken有效期可以设置长一些,例如10天、20天,作为刷新AccessToken的凭证 刷新方案:当AccessToken即将过期的时候,例如提前30分钟,客户端利用RefreshToken请求指定的API获取新的AccessToken并更新本地存储中的AccessToken 核心逻辑 1、登录成功后,jwt生成AccessToken; UUID生成RefreshToken并存储在服务端redis中,设置过期时间 2、接口返回3个字段AccessToken/RefreshToken/访问令牌过期时间戳 3、由于RefreshToken存储在服务端redis中,假如这个RefreshToken也过期,则提示重新登录; 老王的疑问:RefreshToken有效期那么长,和直接将AccessToken的有效期延长有什么区别 答:RefreshToken不像AccessToken那样在大多数请求中都被使用,主要是本地检测accessToken快过期的时候才使用, 一般本地存储的时候,也不叫refreshToken,前端可以取个别名,混淆代码让不能直接识别这个就是刷新令牌 缺点:前端每次请求需要判断token距离过期时间 优点:后端压力小,代码逻辑改动不大
后端存储AccessToken,每次请求过来都判断是否要过期,如果快要过期则重新生成新的token,并返回给前端重新存储,比如距离1天就过期的情况,如果用户访问对应的接口则会更新,但假如没访问则token已经过期则需要重新登录
优点:前端改动小,只需要存储响应http头里面是否有新的令牌产生,有的话就重新存储
缺点:后端实现复杂,且泄露后容易存在一直保活状态,且前端会存在并发请求,当并发请求收到多个jwt token时,容易生成多个token混乱使用
讲解:JWT令牌泄露解决方案-避免盗用
解密:使用互联网大厂的产品时经常遇到这个情况
如何避免token令牌泄露
生成token的时候,加密的payload加入当前用户ip。
拦截器解密后,获取payload的ip和当前访问ip判断是否同个,如果不是则提示重新登录
优点:服务端无需存储相关内容,性能高,假如用户广州登录,泄露了token,依旧用不了
缺点:如果用户用使用过程中ip变动频繁,则操作会经常提示重新登录,体验不友好
当然也可以让用户开启安全模式和非安全模式,让用户自己知道这个情况,一些区块链、比特币交易所里面就会让用户自己选择控制这个token令牌安全是否和ip、终端、地理网络信息进行绑定
{ id:888, name:'小D', ip:“128.23.12.31” expire:10000 } funtion 加密(object, appsecret){ xxxx return base64( token); } function 解密(token ,appsecret){ xxxx //成功返回true,失败返回false }
简介:用户微服务登录拦截器开发
开发登录拦截器
SpringBoot拦截器代码开发
@Slf4j public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { try { String accessToken = request.getHeader("token"); if (accessToken == null) { accessToken = request.getParameter("token"); } if (StringUtils.isNotBlank(accessToken)) { Claims claims = JWTUtil.checkJWT(accessToken); if (claims == null) { //告诉登录过期,重新登录 CommonUtil.sendJsonMessage(response, JsonData.buildError("登录过期,重新登录")); return false; } Long id = Long.valueOf( claims.get("id").toString()); String headImg = (String) claims.get("head_img"); String mail = (String) claims.get("mail"); String name = (String) claims.get("name"); //TODO 用户信息传递 return true; } } catch (Exception e) { log.error("拦截器错误:{}",e); } CommonUtil.sendJsonMessage(response, JsonData.buildError("token不存在,重新登录")); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
简介:玩转ThreadLocal核心知识和应用场景
全称thread local variable(线程局部变量)功用非常简单,使用场合主要解决多线程中数据因并发产生不一致问题。
ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某时间访问到的并不是同一个对象,这样就隔离了多个线程对数据的数据共享,这样的结果是耗费了内存,但大大减少了线程同步所带来性能消耗,也减少了线程并发控制的复杂度。
总结起来就是:同个线程共享数据
注意:ThreadLocal不能使用原子类型,只能使用Object类型
ThreadLocal 用作每个线程内需要独立保存信息,方便同个线程的其他方法获取该信息的场景。
每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参,类似于全局变量的概念,比如用户登录令牌解密后的信息传递(还有用户权限信息、从用户系统获取到的用户名、用户ID)
public static ThreadLocal<LoginUser> threadLocal = new ThreadLocal<>();
LoginUser loginUser = new LoginUser();
loginUser.setId(id);
loginUser.setName(name);
loginUser.setMail(mail);
loginUser.setHeadImg(headImg);
threadLocal.set(loginUser);
简介:ThreadLocal底层源码+原理讲解
原理+源码介绍
ThreadLocal中的一个内部类ThreadLocalMap,这个类没有实现map接口,就是一个普通的Java类,但是实现的类似map的功能
每个数据用Entry保存,其中的Entry继承与WeakReference,用一个键值对存储,键为ThreadLocal的引用。
每个线程持有一个ThreadLocalMap对象,每一个新的线程Thread都会实例化一个ThreadLocalMap并赋值给成员变量threadLocals,使用时若已经存在threadLocals,则直接使用已经存在的对象。
简介:ThreadLocal常见核心面试题
P6面试题:ThreadLocal和Synchronized的区别
P6面试题:为啥什么ThreadLocal的键是弱引用,如果是强引用有什么问题?
Java中除了基础的数据类型以外,其它的都为引用类型。 而Java根据其生命周期的长短将引用类型又分为强引用 、 软引用 、 弱引用 、 虚引用 正常情况下我们平时基本上我们只用到强引用类型,而其他的引用类型我们也就在面试中,或者平日阅读类库或其他框架源码的时候才能见到 1、强引用 new了一个对象就是强引用 Object obj = new Object(); 2、软引用的生命周期比强引用短一些,通过SoftReference类实现,当内存空间足够,垃圾回收器就不会回收它; 当JVM认为内存空间不足时,就会去试图回收软引用指向的对象,也就是说在JVM抛出OutOfMemoryError之前,会去清理软引用对象 主要用来描述一些【有用但并不是必需】的对象 使用场景:适合用来实现缓存,内存空间充足的时候将数据缓存在内存中,如果空间不足了就将其回收掉 3、弱引用是通过WeakReference类实现的,它的生命周期比软引用还要短,在GC的时候,不管内存空间足不足都会回收这个对象 使用场景:一个对象只是偶尔使用,希望在使用时能随时获取,但也不想影响对该对象的垃圾收集,则可以考虑使用弱引用来指向该对象。 ThreadLocal为什么是WeakReference呢? 如果是强引用,即使把ThreadLocal设置为null,但是ThreadLocalMap还持有ThreadLocal的强引用,如果没有手动删除,ThreadLocal不会被回收,导致Entry内存泄漏 如果是弱引用 引用ThreadLocal的对象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用,即使没有手动删除,ThreadLocal也会被回收。value在下一次ThreadLocalMap调用set、get、remove的时候会被清除。
简介:用户微服务登录拦截器路径配置和开发
@Configuration @Slf4j public class InterceptorConfig implements WebMvcConfigurer { public LoginInterceptor loginInterceptor(){ return new LoginInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor()) //拦截的路径 .addPathPatterns("/api/user/*/**","/api/address/*/**") //排查不拦截的路径 .excludePathPatterns("/api/user/*/send_code","/api/user/*/captcha", "/api/user/*/register","/api/user/*/login","/api/user/*/upload"); } }
简介:用户微服务个人信息查询接口开发
简介:用户微服务新增收货地址模块开发
需求
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED) @Override public int add(AddressAddRequest addressAddRequest) { LoginUser loginUser = LoginInterceptor.threadLocal.get(); AddressDO addressDO = new AddressDO(); addressDO.setCreateTime(new Date()); BeanUtils.copyProperties(addressAddRequest, addressDO); addressDO.setUserId(loginUser.getId()); //是否有默认收货地址 if (addressDO.getDefaultStatus() == 1) { AddressDO defaultAddressDO = addressMapper.selectOne(new QueryWrapper<AddressDO>().eq("user_id", loginUser.getId()).eq("default_status", 1)); if (defaultAddressDO != null) { //修改为非默认地址 defaultAddressDO.setDefaultStatus(0); addressMapper.update(defaultAddressDO, new QueryWrapper<AddressDO>().eq("id", defaultAddressDO.getId())); } } int rows = addressMapper.insert(addressDO); return rows; }
简介:用户微服务收货地址查找接口开发和删除地址接口
@Override
public AddressVO findById(long addressId) {
AddressDO addressDO = addressMapper.selectOne(new QueryWrapper<AddressDO>().eq("id", addressId)
if (addressDO == null) {
return null;
}
AddressVO addressVO = new AddressVO();
BeanUtils.copyProperties(addressDO, addressVO);
return addressVO;
}
@Override
public int del(long addressId) {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
int rows = addressMapper.delete(new QueryWrapper<AddressDO>().eq("id", addressId).eq("user_id", loginUser.getId()));
return rows;
}
/** * 通用操作码 */ OPS_REPEAT(110001,"重复操作"), /** * 购物车 */ CART_FAIL(220001,"添加购物车失败"), /** *验证码 */ CODE_TO_ERROR(240001,"接收号码不合规"), CODE_LIMITED(240002,"验证码发送过快"), CODE_ERROR(240003,"验证码错误"), CODE_CAPTCHA(240101,"图形验证码错误"), /** * 账号 */ ACCOUNT_REPEAT(250001,"账号已经存在"), ACCOUNT_UNREGISTER(250002,"账号不存在"), ACCOUNT_PWD_ERROR(250003,"账号或者密码错误"), /** * 优惠券 */ 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,"用户头像文件上传失败");
简介:用户微服务列举指定用户全部收货地址接口开发
@Override
public List<AddressVO> listUserAllAddress() {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
List<AddressDO> list = addressMapper.selectList(new QueryWrapper<AddressDO>().eq("user_id", loginUser.getId()));
List<AddressVO> addressVOList = list.stream().map(obj -> {
AddressVO addressVO = new AddressVO();
BeanUtils.copyProperties(obj, addressVO);
return addressVO;
}).collect(Collectors.toList());
return addressVOList;
}
简介:基于原先编写的接口,指出存在问题
简介:介绍水平-垂直代码防范
介绍
是Web应用程序中一种常见的漏洞,由于其存在范围广、危害大, 列为Web应用十大安全隐患的第二名
指应用在检查授权时存在纰漏,使获得低权限用户账户后,利用一些方式绕过权限检查,访问或者操作其他用户或者更高权限.
产生原因:主要是因为开发人员在对数据进行增、删、改、查询时对客户端请求的数据过分相信,而遗漏了权限的判定
大家检查下自己项目和公司是否有这个问题
防范水平
建立用户和可操作资源的绑定关系,用户对任何资源进行操作时,通过该绑定关系确保该资源是属于该用户所有的
基于RBAC角色访问控制机制来防止纵向越,定义不同的权限角色,为每个角色分配不同的权限,当用户执行某个动作或产生某种行为时,通过用户所在的角色判定该动作或者行为是否允许。
简介:修复用户微服务下水平权限问题
建立用户和可操作资源的绑定关系,用户对任何资源进行操作时,通过该绑定关系确保该资源是属于该用户所有的
ride
public List listUserAllAddress() {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
List list = addressMapper.selectList(new QueryWrapper().eq(“user_id”, loginUser.getId()));
List<AddressVO> addressVOList = list.stream().map(obj -> {
AddressVO addressVO = new AddressVO();
BeanUtils.copyProperties(obj, addressVO);
return addressVO;
}).collect(Collectors.toList());
return addressVOList;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。