「学习笔记」环境配置 - Redis与MySQL多实例配置

最近由于工作的需要,需要在同一台服务器上搭建两个RedisMySQL的实例。 多实例:就是在一台机器上面开启多个不同的端口(如Redis6379/6380MySQL3306/3307等),运行多个服务进程;公用一套安装程序,使用不同的配置文件,数据文件。

1. Redis多实例配置

1.1 查看主机Redis信息
  1. ps命令查看Redis进程
[root@localhost ~] ps -ef |grep redis
root      1706     1  0  2019 ?        04:12:09 /usr/local/bin/redis-server *:6379                    
root     18174  2560  0 15:35 pts/0    00:00:00 grep redis
  1. 查找配置文件位置
[root@localhost ~] locate redis.conf
/etc/redis.conf
1.2 拷贝配置文件并修改
  1. 拷贝redis.conf并命名为redis6380.conf,并修改参数
[root@localhost ~] cp /etc/redis.conf /etc/redis6380.conf
[root@localhost ~] vim /etc/redis6380.conf
# 查找 /pidfile 找到pid位置
# pidfile /var/run/redis.pid        #修改pid,每个实例需要运行在不同的pid
pidfile /var/run/redis6380.pid
# 
# 查找 /port 6379 找到端口位置
# port 6379                         #修改端口
port 6380
#                      
# 查找 /dir 找到数据目录位置
# dir /mnt/newdatadrive/data/redis  #修改数据存放目录
dir /mnt/newdatadrive/data/redis6380
# 
# 已开启Redis持久化
appendonly yes
  1. 准备上面配置的文件
[root@localhost ~] mkdir –p /mnt/newdatadrive/data/redis6380
[root@localhost ~] cp /var/run/redis.pid /var/run/redis6380.pid
1.3 启动测试
  1. 启动6380端口Redis服务,并查看Redis进程
[root@localhost ~] /usr/local/bin/redis-server /etc/redis6380.conf
[root@localhost ~] ps -ef |grep redis
root      1706     1  0  2019 ?        04:12:00 /usr/local/bin/redis-server *:6379         
root     15967     1  0 12:16 ?        00:00:00 /usr/local/bin/redis-server *:6380             
root     15994  8014  0 12:16 pts/2    00:00:00 grep redis
  1. 测试登录Redis客户端
[root@localhost ~] redis-cli -p 6380
127.0.0.1:6380> QUIT     #退出
  1. 停止6380端口的Redis服务
redis-cli -p 6380 shutdown
1.4 Redis数据迁移
  1. 登录原Redis客户端(6379)
[root@localhost ~] redis-cli -p 6379
127.0.0.1:6379> SAVE             #数据备份
127.0.0.1:6379> CONFIG GET dir   #查看Redis数据目录
1) "dir"
2) "/mnt/newdatadrive/data/redis"
127.0.0.1:6379> QUIT             #退出
  1. 拷贝数据文件appendonly.aofdump.rdb6380
# 查看6379的数据文件
[root@localhost ~] cd /mnt/newdatadrive/data/redis && ll
total 55176
-rw-r--r-- 1 root root 55411226 Feb 11 09:25 appendonly.aof
-rw-r--r-- 1 root root  1017181 Feb 11 12:28 dump.rdb
# 拷贝到6380
[root@localhost ~] \cp /mnt/newdatadrive/data/redis/appendonly.aof /mnt/newdatadrive/data/redis6380/appendonly.aof
[root@localhost ~] \cp /mnt/newdatadrive/data/redis/dump.rdb /mnt/newdatadrive/data/redis6380/dump.rdb
  1. 启动6380端口Redis服务,导入AOF数据文件
[root@localhost ~] /usr/local/bin/redis-server /etc/redis6380.conf
[root@localhost ~] redis-cli -p 6380 --pipe < /mnt/newdatadrive/data/redis6380/appendonly.aof
  1. 登录Redis查看数据
[root@localhost ~] redis-cli -p 6380
127.0.0.1:6380>   #输入具体命令查看数据
1.5 配置远程可访问
  1. 修改配置文件redis6380.conf
[root@localhost ~] vim /etc/redis6380.conf
# 查找 /bind 找到:bind 127.0.0.1并注释,其它ip地址也可访问
# bind 127.0.0.1
# 
# 查找 /requirepass 去掉注释#,并把foobared 替换为密码,例如:password123456
# requirepass foobared
requirepass password123456
  1. 开启防火墙的端口号规则(安全组),将6380端口号开通
[root@localhost ~] /sbin/iptables -I INPUT -p tcp --dport 6380 -j ACCEPT
  1. 修改完成后,要在服务里重启Redis服务才能使设置生效
/usr/local/bin/redis-server /etc/redis6380.conf
  1. 测试远程访问
C:\Users\zc> redis-cli -h 192.168.111.226 -p 6380 -a password123456
192.168.111.226:6380>
  1. 停止6380Redis服务也需要密码
[root@localhost ~] redis-cli -p 6380 -a password123456 shutdown

2. MySQL多实例配置

2.1 查看主机MySQL信息
  1. 查看现有MySQL数据库实例占用端口
[root@localhost ~] netstat -anp | grep mysqld
tcp6       0      0 :::3306                 :::*                    LISTEN      1089/mysqld         
unix  2      [ ACC ]     STREAM     LISTENING     20497    1089/mysqld          /var/lib/mysql/mysql.sock

须先关闭单实例,跟多实例会有冲突

「工程实践」基于Shiro前后端分离的认证与授权(下.前端篇)

前两篇我们整合了SpringBoot+Shiro+JWT+Redis实现了登录认证,接口权限控制,接下来将要实现前端 Vue 的动态路由控制。

1. 前端权限控制思路(Vue)

前端的权限控制,不同的权限对应着不同的路由,同时菜单也需根据不同的权限,异步生成。 先回顾下整体流程: /articles/2019/authenticate_shiro_vue/auth-global.png

「工程实践」基于Shiro前后端分离的认证与授权(中.授权篇)

前面我们整合了SpringBoot+Shiro+JWT实现了登录认证,但还没有实现权限控制,这是接下来的工作。

1. JWT的Token续签

1.1 续签思路
  1. 业务逻辑:
    • 登录成功后,用户在未过期时间内继续操作,续签token。
    • 登录成功后,空闲超过过期时间,返回token已失效,重新登录。
  2. 实现逻辑:
    1. 登录成功后将token存储到redis里面(这时候k、v值一样都为token),并设置过期时间为token过期时间
    2. 当用户请求时token值还未过期,则重新设置redis里token的过期时间。
    3. 当用户请求时token值已过期,但redis中还在,则JWT重新生成token并覆盖v值(这时候k、v值不一样了),然后设置redis过期时间。
    4. 当用户请求时token值已过期,并且redis中也不存在,则用户空闲超时,返回token已失效,重新登录。
1.2 编码实现
  1. pom.xml引入Redis
<!-- Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.8.0</version>
</dependency>
  1. 编写Redis工具类
@Component
public class RedisUtil {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    /**
     * 指定缓存失效时间
     * @param key  键
     * @param time 时间(秒)
     */
    public boolean expire(String key, long time) {
        try {
            if (time > 0) {
                redisTemplate.expire(key, time, TimeUnit.SECONDS);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    /**
     * 根据key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0) {
                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}
  1. JwtUtil中增加返回过期秒数的方法
public class JwtUtil {
    /** 设置过期时间: 30分钟 */
    private static final long EXPIRE_TIME = 30 * 60 * 1000;
    //... 其他代码省略
    /**
     * 返回设置的过期秒数
     * @return long 秒数
     */
    public static long getExpireTime(){
        return  EXPIRE_TIME/1000;
    }
}
  1. 改写登录逻辑,生成token后存入Redis
@Service
public class SysServiceImpl implements SysService {
    private String getToken(User user){
        // 生成token
        String token = JwtUtil.createToken(user);
        // 为了过期续签,将token存入redis,并设置超时时间
        redisUtil.set(token, token, JwtUtil.getExpireTime());
        return token;
    }
    /**
     * 用户登录(用户名,密码)
     * @param account 用户名
     * @param password 密码
     * @return token
     */
    @Override
    public ResponseVo<String> login(String account, String password) {
        //处理比对密码
        User user = sysDao.selectByAccount(account);
        if(user!=null) {
            String  salt = user.getSalt();
            String md5Password = Md5Util.md5(password+salt);
            String dbPassword = user.getPassword();
            if(md5Password.equals(dbPassword)) {
                //生成token给用户,并存入redis
                String token = getToken(user);
                return new ResponseVo<>(0,"登录成功", token);
            }
        }
        return new ResponseVo<>( -1, "登录失败");
    }
}
  1. 改写MyRealm,加入token续签逻辑
@Slf4j
@Component("MyRealm")
public class MyRealm extends AuthorizingRealm {
    /**
     * JWT Token续签:
     * 业务逻辑:登录成功后,用户在未过期时间内继续操作,续签token。
     *         登录成功后,空闲超过过期时间,返回token已失效,重新登录。
     * 实现逻辑:
     *    1.登录成功后将token存储到redis里面(这时候k、v值一样都为token),并设置过期时间为token过期时间
     *    2.当用户请求时token值还未过期,则重新设置redis里token的过期时间。
     *    3.当用户请求时token值已过期,但redis中还在,则JWT重新生成token并覆盖v值(这时候k、v值不一样了),然后设置redis过期时间。
     *    4.当用户请求时token值已过期,并且redis中也不存在,则用户空闲超时,返回token已失效,重新登录。
     */
    public boolean tokenRefresh(String token, User user) {
        String cacheToken = String.valueOf(redisUtil.get(token));
        // 过期后会得到"null"值,所以需判断字符串"null"
        if (cacheToken != null && cacheToken.length() != 0 && !"null".equals(cacheToken)) {
            // 校验token有效性
            if (!JwtUtil.isVerify(cacheToken)) {
                // 生成token
                String newToken = JwtUtil.createToken(user);
                // 将token存入redis,并设置超时时间
                redisUtil.set(token, newToken, JwtUtil.getExpireTime());
            } else {
                // 重新设置超时时间
                redisUtil.expire(token, JwtUtil.getExpireTime());
            }
            log.info("打印存入redis的过期时间:"+redisUtil.getExpire(token));
            return true;
        }
        return false;
    }
    /**
     * 重写认证逻辑
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {
        log.info("————————身份认证——————————");
        String token = (String) auth.getCredentials();
        if (null == token) {
            throw new AuthenticationException("token为空!");
        }
        // 解密获得username,用于和数据库进行对比
        String account = JwtUtil.parseTokenAud(token);
        User user = sysService.selectByAccount(account);
        if (null == user) {
            throw new AuthenticationException("用户不存在!");
        }
        // 校验token是否过期
        if (!tokenRefresh(token, user)) {
            throw new AuthenticationException("Token已过期!");
        }
        return new SimpleAuthenticationInfo(user, token,"MyRealm");
    }
}

到此,JWT的Token续签的功能已经全部实现了。

0%