文章目錄
一、Redis常用命令操作
1、String類型
賦值命令:set key value
取值命令:get key
數字遞增:incr key
數字遞減:incr key
增加指定的整數:incrby key increment
減少指定的整數:decrby key decrement
E:\Redis>redis-cli.exe
127.0.0.1:6379> set test:name zhangsan
OK
127.0.0.1:6379> get test:name
"zhangsan"
127.0.0.1:6379> incr num -- 自動初始化num,並返回加1後的結果
(integer) 1
127.0.0.1:6379> decr num -- 返回減1後的結果
(integer) 0
127.0.0.1:6379> incrby num 5 -- 返回加5後的結果
(integer) 5
127.0.0.1:6379> decrby num 4 -- 返回減5後的結果
(integer) 1
127.0.0.1:6379>
2、Hash類型
賦值命令:hset key field value 返回1表示新的 Field 被設置了新值,0表示Field已經存在,用新值覆蓋原有值
取值指令:hget key field
刪除指令:hdel key field
127.0.0.1:6379> hset test:teacher username zhangsan -- value是hash類型包括key-value
(integer) 1
127.0.0.1:6379> hget test:teacher username
"zhangsan"
127.0.0.1:6379> hset test:person id 1
(integer) 1
127.0.0.1:6379> hget test:person id
"1"
127.0.0.1:6379> hset test:person id 2
(integer) 0
127.0.0.1:6379> hdel test:person id
(integer) 1
3、列表類型list
list可以做成棧和隊列,如果是左進右出就是棧,左進左出就是隊列
向列表頭部添加元素:lpush key value [value … ] 插入成功後返回列表中的元素個數
向列表尾部添加元素:rpushkey value [value …] 插入成功後返回列表中的元素個數
元素從左邊出棧 : lpop key 第一步是將列表左邊的元素從列表中移除,第二步是返回被移除的元素值
元素從右邊出棧 : rpop key 第一步是將列表右邊的元素從列表中移除,第二步是返回被移除的元素值
獲取列表中元素的個數:llen key
獲取指定索引的元素值:lindex key index 該命令將返回鏈表中指定位置(index)的元素,index 是0-based
獲得列表:lrange key start stop 返回指定範圍內元素的列表。
127.0.0.1:6379> lpush list 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lpop list
"5"
127.0.0.1:6379> llen list
(integer) 4
127.0.0.1:6379> lindex list 2
"2"
127.0.0.1:6379> rpop list
"1"
4、無序集合set
增加元素:sadd key memer 返回實際插入到集合中元素的個數
獲得集合中元素個數:scard key
從集合中彈出一個元素:spop key 返回移除的成員 (任意的)
判斷元素是否在集合中:sismemer key member 返回1表示已經存在,0表示不存在
127.0.0.1:6379> sadd myset 1
(integer) 1
127.0.0.1:6379> sadd myset 2 3 4
(integer) 3
127.0.0.1:6379> scard myset
(integer) 4
127.0.0.1:6379> spop myset
"2"
127.0.0.1:6379> sismember myset 3
(integer) 1
127.0.0.1:6379>
5、有序集合sorted sort
增加元素:zadd key score member [score] [member ] 添加成功返回實際插入的成員數量
獲得集合中元素個數:zcard key
獲得指定分數範圍內的元素個數:zcount key min max 該命令用於獲取分數(score)在min和max之間的成員數量
獲得元素的分數:zscore key member
增加某個元素的分數:zincrby key increment member
獲得元素的排名:zrank key member
127.0.0.1:6379> zadd myzset 1 a 2 b 3 c
(integer) 3
127.0.0.1:6379> zcard myzset
(integer) 3
127.0.0.1:6379> zcount myzset 1 3
(integer) 3
127.0.0.1:6379> zscore myzset a
"1"
127.0.0.1:6379> zscore myzset c
"3"
127.0.0.1:6379> zincrby myzset 2 a
"3"
127.0.0.1:6379> zscore myzset a
"3"
127.0.0.1:6379> zrank myzset a
(integer) 1
127.0.0.1:6379> zrank myzset c
(integer) 2
127.0.0.1:6379>
二、分佈式共享session
實現思路:
(1) 登錄頁面提交用戶名密碼。
(2) 登錄成功後生成token。Token相當於原來的sessionid,字符串,可以使用uuid。
(3) 把用戶信息保存到redis。Key就是token,value就是userId。
(4) 設置key的過期時間。模擬Session的過期時間。一般一個小時。
(5) 攔截器攔截請求校驗 sessionId
1、LoginReqVO 登錄用戶用來提交用戶名和密碼
@Data
public class LoginReqVO {
@ApiModelProperty(value = "用戶名")
private String username;
@ApiModelProperty(value = "密碼")
private String password;
}
2、LoginRespVO 將用戶信息和token響應給瀏覽器
@Data
public class LoginRespVO {
@ApiModelProperty(value = "用戶認證憑證")
private String token;
@ApiModelProperty(value = "用戶id")
private String userId;
}
3、UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public LoginRespVO login(LoginReqVO vo) {
//根據提交的用戶名到數據庫彙總查找該用戶
SysUser sysUser = sysUserMapper.selectByUsername(vo.getUsername());
//如果用戶不存在
if(sysUser==null){
throw new BusinessException(4001005,"不存在該用戶,請先註冊");
}
//如果用戶狀態爲禁用
if(sysUser.getStatus()==2){
throw new BusinessException(4001006,"該賬號已被禁用請聯繫系統管理員");
}
//如果用戶密碼不正確(將明文加密後再與數據庫中的密碼進行比較)
if(!PasswordUtils.matches(sysUser.getSalt(),vo.getPassword(),sysUser.getPassword())){
throw new BusinessException(4001007,"用戶名密碼不匹配");
}
//登錄成功,生成token
String token= UUID.randomUUID().toString();
//將token和用戶信息響應給瀏覽器
LoginRespVO respVO=new LoginRespVO();
respVO.setUserId(sysUser.getId());
respVO.setToken(token);
//將用戶信息在redis緩存中,key是token,值是用戶信息,同時設置token過期時間爲60s
redisTemplate.opsForValue().set(token,sysUser.getId(),60, TimeUnit.MINUTES);
return respVO;
}
}
4、UserController
@RestController
@RequestMapping("/api")
@Api(tags = "用戶模塊")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/user/login")
@ApiOperation(value = "用戶登錄接口")
public LoginRespVO login(@RequestBody LoginReqVO vo){
return userService.login(vo);
}
}
5、配置攔截路徑:登錄註冊請求不能攔截,需要放行
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Bean
public TokenInterceptor tokenInterceptor(){
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor())
.addPathPatterns("/api/**").excludePathPatterns("/api/user/login","/api/user/register","/api/user/code/*");
}
}
6、TokenInterceptor配置攔截器:對於除登錄註冊外的其他請求,都要進行攔截,判斷用戶是否處於登錄狀態。
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisService redisService;
private RedisTemplate redisTemplate;
//在執行Controller層方法之前攔截登錄請求,驗證用戶是否處於登錄狀態
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//從請求頭中獲取token
String token=request.getHeader("token");
//判斷token是否爲空
if(StringUtils.isEmpty(token)){
throw new BusinessException(4001002,"用戶憑證不能爲空,請重新登錄");
}else {
//如果token不爲空,需要判斷token是否有效
if(!redisTemplate.hasKey(token)){
throw new BusinessException(4001002,"用戶憑證無效,請重新登錄");
}
}
return true;
}
}
三、異地登錄提醒下線
最近接到產品提的一個需求說,一個賬號同時只能在一個地方登錄,如果在其他地方登錄則提示已在別處登錄,同時,同一瀏覽器同時只能登錄一個用戶。
實現思路:
(1) 登錄頁面提交用戶名密碼。
(2) 登錄成功後生成token。
(3) 把用戶信息保存到redis。Key就是token,value就是userId。
(4) 設置key的過期時間。
(5) 把token存入redis,key爲 userId,value 就是 token
(6) 攔截器攔截請求校驗 token。
(7) 獲取 userId 後再去比較 header 攜帶的token和redis標記的token是否一致,不一致則提示用戶已經異地登錄。
1、 LoginReqVO
@Data
public class LoginReqVO {
@ApiModelProperty(value = "用戶名")
private String username;
@ApiModelProperty(value = "密碼")
private String password;
}
2、LoginRespVO
@Data
public class LoginRespVO {
@ApiModelProperty(value = "用戶認證憑證")
private String token;
@ApiModelProperty(value = "用戶id")
private String userId;
}
3、 UserServiceImpl :用戶登錄成功後,生成token並響應給瀏覽器
@Service
public class UserServiceImpl implements UserService {
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private RedisTemplate redisTemplate;
@Override
public LoginRespVO login(LoginReqVO vo) {
//根據提交的用戶名到數據庫彙總查找該用戶
SysUser sysUser = sysUserMapper.selectByUsername(vo.getUsername());
//如果用戶不存在
if(sysUser==null){
throw new BusinessException(4001005,"不存在該用戶,請先註冊");
}
//如果用戶狀態爲禁用
if(sysUser.getStatus()==2){
throw new BusinessException(4001006,"該賬號已被禁用請聯繫系統管理員");
}
//如果用戶密碼不正確
if(!PasswordUtils.matches(sysUser.getSalt(),vo.getPassword(),sysUser.getPassword())){
throw new BusinessException(4001007,"用戶名密碼不匹配");
}
//登錄成功,生成token
String token= UUID.randomUUID().toString();
//將token和用戶信息響應給瀏覽器
LoginRespVO respVO=new LoginRespVO();
respVO.setUserId(sysUser.getId());
respVO.setToken(token);
//將用戶信息在redis緩存中,key是token,值是用戶信息,過期時間爲60分鐘
redisTemplate.opsForValue().set(token,sysUser.getId(),60, TimeUnit.MINUTES);
//標記token,把token存入redis緩存中,key是userId,值爲token,過期時間爲60分鐘
redisTemplate.opsForValue().set(sysUser.getId(),token,60,TimeUnit.MINUTES);
return respVO;
}
}
4、UserController:
@RestController
@RequestMapping("/api")
@Api(tags = "用戶模塊")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/user/login")
@ApiOperation(value = "用戶登錄接口")
public LoginRespVO login(@RequestBody LoginReqVO vo){
return userService.login(vo);
}
}
5、配置攔截器攔截路徑:放行登錄
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
@Bean
public TokenInterceptor tokenInterceptor(){
return new TokenInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tokenInterceptor())
.addPathPatterns("/api/**").excludePathPatterns("/api/user/login","/api/user/register","/api/user/code/*");
}
}
6、配置攔截器:判斷前端穿過類的token和redis緩存中標記的token是否一致
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private RedisService redisService;
private RedisTemplate redisTemplate;
//在執行Controller層方法之前攔截登錄請求,驗證用戶是否處於登錄狀態
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//從請求頭中獲取token
String token=request.getHeader("token");
//判斷token是否爲空
if(StringUtils.isEmpty(token)){
throw new BusinessException(4001002,"用戶憑證不能爲空,請重新登錄");
}else {
//判斷緩存中token是否過期
if(!redisTemplate.hasKey(token)){
throw new BusinessException(4001002,"用戶憑證無效,請重新登錄");
}
//根據鍵token獲取值userId
String userId= (String) redisTemplate.opsForValue().get(token);
//如果redis中不存在userId或者瀏覽器傳過來的token和redis緩存中的token不匹配
if(redisTemplate.hasKey(userId)&&!token.equals(redisTemplate.opsForValue().get(userId)){
throw new BusinessException(4001002,"您的賬號已經在異地登錄,請重新登錄");
}
}
return true;
}
}
四、註冊短信驗證碼
短信驗證碼是所有項目必不可少的基礎功能模塊之一,假如突然有一天你領導給你佈置的一個需求。在用戶註冊的時候要校驗手機號。
要求如下:
(1) 註冊的時候校驗手機號
(2) 每個手機號每天最多發送五條註冊短信驗證碼
(3) 驗證碼5分鐘內有效。
思路:
(1) 發送前驗證手機號是否符合要求。
(2) 生成短信驗證碼。
(3) 發送驗證碼到手機。
(4) 把驗證碼存入redis
(5) 標記手機號
(6) 註冊的時候校驗手機號和驗證碼是否正確
1、RegisterReqVO
@Data
public class RegisterReqVO {
@ApiModelProperty(value = "賬號")
private String username;
@ApiModelProperty(value = "手機號")
private String phone;
@ApiModelProperty(value = "密碼")
private String password;
@ApiModelProperty(value = "驗證碼")
private String code;
}
2、 Contant:構建redis的key
*/
public class Contant {
/**
* 判斷是否達上線的key
*/
public final static String REGISTER_CODE_COUNT_KEY="register-code-count-key_";
/**
* 驗證碼有效期key
*/
public final static String REGISTER_CODE_COUNT_VALIDITY_KEY="register-code-count-validity-key_";
}
3、UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
private SysUserMapper sysUserMapper;
@Autowired
private RedisTemplate redisTemplate;
/**
* 獲取驗證碼
*/
@Override
public String getCode(String phone) {
//驗證手機號是否合法
Pattern pattern = Pattern.compile("^1(3|4|5|7|8)\\d{9}$");
Matcher matcher = pattern.matcher(phone);
if(!matcher.matches()) {
throw new BusinessException(4001004,"手機號格式錯誤");
}
//每次獲取驗證碼就讓次數加1,判斷獲取手機號驗證碼是否超限
long count = redisTemplate.opsForValue().increment(Contant.REGISTER_CODE_COUNT_KEY+phone,1);
if(count>5){
throw new BusinessException(4001004,"當日發送已達上限");
}
//生成6位隨機數,作爲驗證碼
String code=generateCode();
//將生成的驗證碼存入 redis 過期時間爲 5 分鐘
redisService.set(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+phone,code,5,TimeUnit.MINUTES;
//發送短信這裏用輸出模擬
System.out.println(code);
return code;
}
/**
* 生成六位驗證碼
*/
private String generateCode(){
Random random = new Random();
int x = random.nextInt(899999);
String code = String.valueOf(x + 100000);
return code;
}
/**
* 用戶註冊
*/
@Override
public String register(RegisterReqVO vo) {
//判斷驗證碼是否有效
if(!redisTemplate.hasKey(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone()){
throw new BusinessException(4001008,"驗證碼已失效請重新獲取");
}
//校驗驗證碼是否正確(前端傳過來的驗證碼和redis緩存中的是否一致)
if(!vo.getCode().equals(redisTemplate.opsForValue().get(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone()))){
throw new BusinessException(4001009,"請輸入正確的驗證碼");
}
SysUser sysUser = sysUserMapper.selectByUsername(vo.getUsername());
if(sysUser!=null){
throw new BusinessException(4001010,"該用戶名已被註冊");
}
//構造user用戶信息存入數據庫中
SysUser user=new SysUser();
//屬性複製
BeanUtils.copyProperties(vo,user);
user.setId(UUID.randomUUID().toString());
user.setCreateTime(new Date());
String salt=PasswordUtils.getSalt();
String ecdPwd=PasswordUtils.encode(vo.getPassword(),salt);
user.setSalt(salt);
user.setPassword(ecdPwd);
int i = sysUserMapper.insertSelective(user);
if(i!=1){
throw new BusinessException(4001011,"操作失敗");
}
//註冊成功時將redis緩存清除
redisTemplate.delete(Contant.REGISTER_CODE_COUNT_VALIDITY_KEY+vo.getPhone());
redisTemplate.delete(Contant.REGISTER_CODE_COUNT_KEY+vo.getPhone());
return "註冊成功";
}
}
4、UserController
@RestController
@RequestMapping("/api")
@Api(tags = "用戶模塊")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/user/register")
@ApiOperation(value = "用戶註冊接口")
public String register(@RequestBody RegisterReqVO vo){
return userService.register(vo);
}
@GetMapping("/user/code/{phone}")
public String getCode(@PathVariable("phone") String phone){
return userService.getCode(phone);
}
}