1. 程式人生 > >【java小程式實戰】redis快取session的實現

【java小程式實戰】redis快取session的實現

###內容目錄(由[TOC]自動生成)

文章目錄

一、有狀態session

當用戶訪問web網站的時候,會產生一個session,維護這使用者和系統的關係

二、無狀態session

當app端的使用者訪問網站的時候,沒有session這個會話,無法維護使用者和系統的關係。

三、redis-session

1、使用者資訊儲存到redis中,形成無狀態會話。
2、便於擴充套件,當單體應用擴充套件成叢集會相當方便。
3、便於許可權驗證。

四、redis應用

先去官網下載redis版本,並安裝,關於安裝的教程,大家可以自行百度。

1、pom依賴

<!-- 引入 redis 依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>2.9.0</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-redis</artifactId>
			<version>1.8.7.RELEASE</version>
		</dependency>

2、application.properties redis的配置

############################################################
#
# redis 配置
#
############################################################
# Redis 資料庫索引(預設為0)
spring.redis.database=1
# Redis伺服器地址
spring.redis.host=localhost
# Redis伺服器埠號
spring.redis.port=6379
#Redis 伺服器連線密碼(預設為空)
spring.redis.password=
# 連線池最大連線數(使用負值表示沒有限制)
spring.redis.pool.max-active=1000
#連線池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
#  連線池中最大空閒連線
spring.redis.pool.max-idle=10
# 連線池中的最小空閒連線
spring.redis.pool.min-idle=2
# 連線超時時間(毫秒)
spring.redis.timeout=0

3、redis的一個工具類

 package com.imooc.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 *   使用redisTemplate 的操作實現類
 */
@Component
public class RedisOperator {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

     // Key (鍵),簡單的 key-value操作

    /**
     *  實現命令:TTL key, 以秒為單位,返回給定的key的剩餘時間)(ttl,time to live)
     * @param key
     * @return
     */
    public long ttl(String key){
        return stringRedisTemplate.getExpire(key);
    }

    /**
     *  實現命令: expire ,設定過期時間,單位秒
     * @param key
     * @param timeout
     */
    public void expire(String key, long timeout) {
        stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 實現命令: incr key ,增加key 一次
     * @param key
     * @param delta
     * @return
     */
    public long incr(String key, long delta){
        return stringRedisTemplate.opsForValue().increment(key,delta);
    }

    /**
     * 實現命令:KEYS pattern, 查詢所有符合給定模式 pattern 的key
     * @param pattern
     * @return
     */
    public Set<String> keys(String pattern){
        return stringRedisTemplate.keys(pattern);
    }

    /**
     * 實現命令:DEL key ,刪除一個key
     * @param key
     */
    public  void del(String key){
           stringRedisTemplate.delete(key);
    }

    /**
     * 實現命令: SET key value,設定一個key-value(將字串值value 關聯到key)
     * @param key
     * @param value
     */
    public void set(String key, String value){
        stringRedisTemplate.opsForValue().set(key, value);
    }

    /**
     *  實現命令:SET key value time,設定一個key-value 過期時間。
     * @param key
     * @param value
     * @param time
     */
    public void set(String key, String value, long time){
        stringRedisTemplate.opsForValue().set(key, value, time);
    }
    /**
     * 實現命令: GET key,返回key所關聯的字串值
     * @param key
     * @return
     */
    public String get(String key){
        return (String)stringRedisTemplate.opsForValue().get(key);
    }

    // Hash (雜湊表)

    /**
     * 實現命令: HSET key field value ,將雜湊表key 中的域 field 的值設為value
     * @param key
     * @param field
     * @param value
     */
    public void hset(String key, String field, Object value){
        stringRedisTemplate.opsForHash().put(key, field, value);
    }

    /**
     * 實現命令:HGET key field,返回雜湊表 key中給定域field的值
     * @param key
     * @param field
     * @return
     */
    public String hget(String key, String field){
        return (String) stringRedisTemplate.opsForHash().get(key, field);
    }

    /**
     * 實現命令: HDEL key field[field...],刪除雜湊表 key 中的一個或多個指定域,不存在的域將被忽略。
     * @param key
     * @param fields
     */
    public void hdel(String key, Object... fields){
        stringRedisTemplate.opsForHash().delete(key, fields);
    }

    /**
     * 實現命令: HGETALL key,返回雜湊表 key中,所有的域和值
     * @param key
     * @return
     */
    public Map<Object, Object> hgetall(String key){
         return stringRedisTemplate.opsForHash().entries(key);
    }

    //List (列表)

    /**
     * 實現命令: LPUSH key value,將一個值value 插入到列表 key 的表頭
     * @param key
     * @param value
     * @return
     */
    public long lpush(String key, String value){
        return  stringRedisTemplate.opsForList().leftPush(key, value);
    }

    /**
     * 實現命令:LPOP key,移出並返回列表 key的頭元素。
     * @param key
     * @return
     */
    public String lpop(String key) {
        return (String)stringRedisTemplate.opsForList().leftPop(key);
    }

    /**
     * 實現命令:RPUSH key value ,將一個值value 插入到列表 key 的表尾
     * @param key
     * @param value
     * @return
     */
    public long rpush(String key, String value){
        return stringRedisTemplate.opsForList().rightPush(key, value);
    }
}

4、在api模組,新建一個vo包,建立UsersVo物件,添加了一個userToken屬性。

package com.imooc.pojo.vo;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import javax.persistence.Column;
import javax.persistence.Id;

@ApiModel(value = "使用者物件" ,description = "這是使用者物件")
public class UsersVo {
    @ApiModelProperty(hidden = true)
    private String id;

    @ApiModelProperty(hidden = true)
    private String userToken;

    /**
     * 使用者名稱
     */
    @ApiModelProperty(value = "使用者名稱" , name = "username" , example = "csdnuser" ,required = true)
    private String username;

    /**
     * 密碼
     */
    @ApiModelProperty(value = "密碼" , name = "password" , example = "123456" ,required = true)
    private String password;

    /**
     * 我的頭像,如果沒有預設給一張
     */
    @ApiModelProperty(hidden = true)
    private String faceImage;

    /**
     * 暱稱
     */
    private String nickname;

    /**
     * 我的粉絲數量
     */
    @ApiModelProperty(hidden = true)
    private Integer fansCounts;

    /**
     * 我關注的人總數
     */
    @ApiModelProperty(hidden = true)
    private Integer followCounts;

    /**
     * 我接受到的讚美/收藏 的數量
     */
    @ApiModelProperty(hidden = true)
    private Integer receiveLikeCounts;

    /**
     * @return id
     */
    public String getId() {
        return id;
    }

    /**
     * @param id
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * 獲取使用者名稱
     *
     * @return username - 使用者名稱
     */
    public String getUsername() {
        return username;
    }

    /**
     * 設定使用者名稱
     *
     * @param username 使用者名稱
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 獲取密碼
     *
     * @return password - 密碼
     */
    public String getPassword() {
        return password;
    }

    /**
     * 設定密碼
     *
     * @param password 密碼
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 獲取我的頭像,如果沒有預設給一張
     *
     * @return face_image - 我的頭像,如果沒有預設給一張
     */
    public String getFaceImage() {
        return faceImage;
    }

    /**
     * 設定我的頭像,如果沒有預設給一張
     *
     * @param faceImage 我的頭像,如果沒有預設給一張
     */
    public void setFaceImage(String faceImage) {
        this.faceImage = faceImage;
    }

    /**
     * 獲取暱稱
     *
     * @return nickname - 暱稱
     */
    public String getNickname() {
        return nickname;
    }

    /**
     * 設定暱稱
     *
     * @param nickname 暱稱
     */
    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    /**
     * 獲取我的粉絲數量
     *
     * @return fans_counts - 我的粉絲數量
     */
    public Integer getFansCounts() {
        return fansCounts;
    }

    /**
     * 設定我的粉絲數量
     *
     * @param fansCounts 我的粉絲數量
     */
    public void setFansCounts(Integer fansCounts) {
        this.fansCounts = fansCounts;
    }

    /**
     * 獲取我關注的人總數
     *
     * @return follow_counts - 我關注的人總數
     */
    public Integer getFollowCounts() {
        return followCounts;
    }

    /**
     * 設定我關注的人總數
     *
     * @param followCounts 我關注的人總數
     */
    public void setFollowCounts(Integer followCounts) {
        this.followCounts = followCounts;
    }

    /**
     * 獲取我接受到的讚美/收藏 的數量
     *
     * @return receive_like_counts - 我接受到的讚美/收藏 的數量
     */
    public Integer getReceiveLikeCounts() {
        return receiveLikeCounts;
    }

    /**
     * 設定我接受到的讚美/收藏 的數量
     *
     * @param receiveLikeCounts 我接受到的讚美/收藏 的數量
     */
    public void setReceiveLikeCounts(Integer receiveLikeCounts) {
        this.receiveLikeCounts = receiveLikeCounts;
    }

    public String getUserToken() {
        return userToken;
    }

    public void setUserToken(String userToken) {
        this.userToken = userToken;
    }
}

5、建立一個BasicController

package com.imooc.controller;

import com.imooc.utils.RedisOperator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class BasicController {
    @Autowired
    public RedisOperator redis;

    public  static final  String USER_REDIS_SESSION  = "user-redis-session";
}

5、對RegistLoginController進行修改

package com.imooc.controller;


import com.imooc.imoocvideosdevservice.com.imooc.service.UserService;
import com.imooc.pojo.Users;
import com.imooc.pojo.vo.UsersVo;
import com.imooc.utils.IMoocJSONResult;
import com.imooc.utils.MD5Utils;
import io.swagger.annotations.Api;

import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.security.NoSuchAlgorithmException;
import java.util.UUID;

//返回格式為json格式
@RestController
@Api(value = "使用者註冊登陸的介面" , tags = {"註冊和登陸的controller"})
public class RegistLoginController extends BasicController {
    @Autowired
    private UserService userService;
    @ApiOperation(value = "使用者註冊", notes = "使用者註冊的介面")
    @PostMapping("/regist")
    public IMoocJSONResult regist(@RequestBody Users user) throws Exception {
        System.out.println("username:"+user.getUsername()+"password:"+user.getPassword());
      //1、判斷使用者名稱和密碼是否為空
        if(StringUtils.isBlank(user.getUsername()) || StringUtils.isBlank(user.getPassword())){
            return IMoocJSONResult.errorMsg("使用者名稱和密碼不能為空");
        }
        //2、判斷使用者名稱是否存在
         boolean usernameIsExist = userService.queryUsernameIsExist(user.getUsername());
        //3、儲存使用者,註冊資訊
        if(!usernameIsExist){
            user.setNickname(user.getUsername());
            user.setPassword(MD5Utils.getMD5Str(user.getPassword()));
            user.setReceiveLikeCounts(0);
            user.setFansCounts(0);
            user.setFollowCounts(0);
            userService.saveUser(user);
        }else{
            return IMoocJSONResult.errorMsg("使用者名稱已經存在");
        }
        //在註冊完畢,將使用者資料返回給前端之前,為了安全,將密碼設為null
        user.setPassword("");
        //使用uuid,設定使用者的value
//        String uniqueToken = UUID.randomUUID().toString();
//        redis.set(USER_REDIS_SESSION + ":" + user.getId(), uniqueToken, 1000 * 60 * 30);
//        UsersVo usersVo = new UsersVo();
//        BeanUtils.copyProperties(user, usersVo);
//        usersVo.setUserToken(uniqueToken);

        // 繫結使用者資訊到redis
        UsersVo usersVo = setUserRedisSessionToken(user);
        return IMoocJSONResult.ok(usersVo);
    }
    @ApiOperation(value= "使用者登陸" , notes = "使用者登陸介面")
    @PostMapping("/login")
    public IMoocJSONResult login(@RequestBody Users users) throws NoSuchAlgorithmException, InterruptedException {
        Thread.sleep(3000);
           String username = users.getUsername();
           String password = users.getPassword();
           //1、判斷使用者名稱和密碼不能為空
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)){
            return IMoocJSONResult.errorMsg("使用者名稱和密碼不能為空");
        }
        // 2、判斷使用者是否存在
        Users userResult = userService.queryUserForLogin(username,MD5Utils.getMD5Str(password));
        //3、返回
        if (userResult !=null ){
            //密碼設定為null,返回給前端
            userResult.setPassword("");
            //將使用者資訊進行redis設定
            UsersVo usersVo = setUserRedisSessionToken(userResult);
            return IMoocJSONResult.ok(usersVo);
        } else {
            return IMoocJSONResult.errorMsg("使用者名稱或密碼不正確,請重試...");
        }

    }

    /**
     * 將使用者資訊進行redis設定
     * @param userModel
     * @return
     */
    public UsersVo setUserRedisSessionToken(Users userModel){
        //使用uuid,設定使用者的value
        String uniqueToken = UUID.randomUUID().toString();
        redis.set(USER_REDIS_SESSION + ":" + userModel.getId(), uniqueToken, 1000 * 60 * 30);
        UsersVo usersVo = new UsersVo();
        BeanUtils.copyProperties(userModel, usersVo);
        usersVo.setUserToken(uniqueToken);
        return usersVo;
    }
}

現在我們就可以重啟後端程式碼,通過小程式訪問。之後可以在Redis-Desktop Manager工具中檢視,快取的使用者資訊。
備註: 在controller中我們通過 一個 :(冒號)將同一類資訊,歸屬到同一個資料夾下。

在這裡插入圖片描述