项目中分哪几种用户角色,应该有哪些角色
普通用户和管理员
不同的角色要考虑设置什么权限呢
普通用户:选购商品,查看修改购物车、修改个人资料、密码,支付等。
管理员:上下架商品,找回用户密码,退货
角色、权限怎么设计它们的存储
RBAC角色权限:角色一张表,用户一张表,权限一张表,然后用户-角色、角色-权限分别一张表,表示对应关系。
一个用户对应哪几种角色呢
当时没想到RBAC,用RBAC的话只需要再用户-角色表里添加对应关系即可。
用户发过来请求,怎么判断这个用户是谁
利用JWT的payload识别用户ID
请求头怎么表示用户是谁
微服务传递时在请求头Authorization字段加上用户ID
JWT怎么识别出当前用户是谁
用户信息包含在载荷中:
当用户登录或进行身份验证时,服务器会生成一个JWT,并在载荷部分包含用户的唯一标识符(如用户ID)客户端发送JWT:
客户端(如浏览器或移动应用)在随后的请求中,会将JWT作为HTTP请求头(如Authorization: Bearer <token>
)或请求体的一部分发送给服务器。服务器验证JWT:
服务器接收到请求后,会首先验证JWT的签名以确保令牌未被篡改。验证通过后,服务器会解析JWT的载荷部分,从中获取用户信息(如user_id
),并根据这些信息来识别当前用户。
JWT怎么防止被伪造
强密钥:JWT的签名部分使用密钥(secret)进行加密。确保使用足够长的、复杂的密钥,避免使用弱密钥或默认密钥。
一个用户被盗号了,修改密码,怎么把之前的JWT作废
1. 使用黑名单或白名单机制
策略描述:
- 黑名单:
- 在用户修改密码后,将旧的JWT放入黑名单中,并存储在如Redis这样的内存数据库中。
- 设置黑名单中JWT的过期时间与原始JWT的过期时间相同,以减少服务器压力。
- 在每次请求验证JWT时,先检查该JWT是否在黑名单中,如果在则拒绝访问。
- 白名单:
- 与黑名单相反,只存储有效的JWT在Redis等内存数据库中。
- 用户修改密码后,移除所有旧JWT的存储,只保留新JWT。
- 但这种方法需要确保在每次用户认证成功后都更新白名单,且处理逻辑较为复杂。
优点:
- 可以立即作废JWT,防止被恶意使用。
- 灵活性强,可以根据实际情况选择使用黑名单或白名单。
缺点:
- 需要额外的存储空间和计算资源来维护黑名单或白名单。
- 增加了系统的复杂性。
2. 结合使用JWT和Refresh Token
策略描述:
- 在用户登录时,不仅颁发一个JWT作为访问令牌,还颁发一个Refresh Token。
- JWT的过期时间设置得较短(如几分钟到几小时),而Refresh Token的过期时间设置得较长(如几天到几个月)。
- 当JWT过期时,客户端可以使用Refresh Token向服务器请求一个新的JWT。
- 在用户修改密码后,使所有之前的Refresh Token失效(可以通过更改用户数据库中的某个字段或状态来实现)。
优点:
- 可以在不影响用户体验的情况下,实现JWT的定期更换和作废。
- 减少了JWT被盗用的风险。
缺点:
- 需要额外的逻辑来处理Refresh Token的颁发和验证。
3. 更新用户数据库中的状态
策略描述:
- 在用户修改密码时,更新用户数据库中的某个字段(如“最后密码修改时间”)。
- 在验证JWT时,除了检查JWT的签名和过期时间外,还检查JWT中携带的用户信息与数据库中的信息是否一致(特别是与密码修改时间相关的字段)。
优点:
- 无需额外的存储空间来维护黑名单或白名单。
- 只需在用户数据库中进行简单的状态更新。
缺点:
- 需要修改JWT的验证逻辑,使其包含对数据库状态的检查。
- 可能会影响JWT验证的性能。
找当前所在位置周边指定距离内的店铺,怎么实现
Redis的GEO数据结构,当时忘了,一直用数据库
操作系统中程序的内存在它看来是连续的,但是实际上在物理内存上是不连续的,这是怎么实现的。
操作系统中程序的内存在它看来是连续的,但实际上在物理内存上是不连续的,这一特性主要通过虚拟内存技术和内存管理单元(Memory Management Unit, MMU)来实现。以下是具体的实现方式:
1. 虚拟内存技术
虚拟内存是现代操作系统普遍使用的一种技术,它允许程序拥有比实际物理内存更大的地址空间。虚拟内存通过将物理内存和磁盘空间(或其他类型的存储介质)组合起来,为程序提供一个统一的、连续的虚拟地址空间。
- 地址空间抽象:操作系统为每个运行的程序创建一个独立的地址空间(Address Space),该空间包含了程序的所有相关内存,如代码区、数据区、堆栈区等。这些区域在逻辑上是连续的,但在物理上可能并不连续。
- 内存映射:操作系统维护一个从虚拟地址到物理地址的映射表(如页表),用于将程序访问的虚拟地址转换为实际的物理地址。当程序访问某个内存地址时,操作系统会查找映射表,找到对应的物理地址,然后访问该物理地址上的数据。
2. 内存管理单元(MMU)
内存管理单元(MMU)是硬件组件,负责处理CPU的内存访问请求,实现虚拟地址到物理地址的转换。
- 地址转换:当CPU发出内存访问请求时,MMU会拦截该请求,并根据映射表将请求的虚拟地址转换为物理地址。如果映射表中没有相应的条目(即发生了缺页中断),MMU会通知操作系统进行处理(如从磁盘加载相应的页面到物理内存中)。
- 缓存优化:为了提高地址转换的效率,MMU通常会包含一个翻译后备缓冲器(Translation Lookaside Buffer, TLB),用于缓存最近访问的页表项。当CPU访问某个内存地址时,如果TLB中已有该地址的缓存条目,则可以直接从TLB中获取物理地址,无需再次访问页表。
3. 非连续内存分配的优势
- 更好的内存利用和管理:非连续内存分配允许程序使用物理内存中的不连续区域,从而减少了内存碎片的产生,提高了内存的利用率。
- 支持动态加载和动态链接:程序可以在运行时动态地加载和链接库文件,这些库文件可以分布在物理内存中的不同位置,而无需考虑物理内存的连续性。
操作系统的锁有哪些实现
互斥锁、读写锁、自旋锁、乐观锁和悲观锁、可重入锁
所有的KV过期时间相同,所以任何时刻LRU后面节点剩余过期时间比前面节点少,题目要求随意剔除,所以剔除最后一个,还是按照LRU的规则。就是多了个创建时间的属性。
get时用当前时间减去,和过期间隔比一下,看是否过期,过期返回空,否则更新并重置创建时间。
put则和普通LRU一致。
package org.example; import java.time.Duration; import java.time.Instant; import java.util.HashMap; import java.util.LinkedList; class LRUCache { class Node{ public int key,val; Instant time; public Node pre,next; Node(){} } LinkedList<Node> Lru; HashMap<Integer,Node> map; //当前大小,最大容量,过期时间 int cnt=0,maxn=0,time_gap; Node head,end; public LRUCache(int capacity) { Lru=new LinkedList<Node>(); map=new HashMap<Integer,Node>(); maxn=capacity; head=new Node(); end=new Node(); head.next=end; end.pre=head; time_gap=3; } public int get(int key) { Node node=map.get(key); // System.out.println(Duration.between(node.time,Instant.now()).getSeconds()); //不存在或过期 if(node==null|| Duration.between(node.time,Instant.now()).getSeconds()>time_gap){ return -1; } move_to_head(node); return node.val; } public void put(int key, int value) { Node node=map.get(key); if(node!=null){//更改值 node.val=value; move_to_head(node); return; } if(cnt==maxn){//淘汰 cnt-=1; Node last=end.pre; delete(last); map.remove(last.key); } node=new Node();//新增节点 node.val=value; node.key=key; node.time=Instant.now(); map.put(key,node); move_to_head(node);// cnt+=1; } void move_to_head(Node node){//移到开头,更新过期时间 if(node.pre!=null){// delete(node); } Node first=head.next; head.next=node; node.next=first; node.pre=head; first.pre=node; node.time=Instant.now(); } void delete(Node node){//将节点前后跳过 Node p=node.pre; Node n=node.next; p.next=n; n.pre=p; } public static void main(String[] args) throws InterruptedException { LRUCache lruCache = new LRUCache(3); lruCache.put(1,2); lruCache.put(2,3); lruCache.put(3,4); lruCache.put(4,5); System.out.println(lruCache.get(2)); Thread.sleep(5*1000); System.out.println(lruCache.get(1)); System.out.println(lruCache.get(2)); } }