用redis+jwt保存在线用户和获得在线用户列表、踢出用户示例

redis工具类

import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisConnectionUtils;
@Component
@SuppressWarnings({
    
    "unchecked", "all"})
public class RedisUtils {
    
    

    private RedisTemplate<Object, Object> redisTemplate;
    @Value("${jwt.online-key}")
    private String onlineKey;

    public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {
    
    
        this.redisTemplate = redisTemplate;
    }

    /**
     * 根据 key 获取过期时间
     * @param key 键 不能为null
     * @return 时间(秒) 返回0代表为永久有效
     */
    public long getExpire(Object key) {
    
    
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

 /**
     * 查找匹配key
     * @param pattern key
     * @return /
     */
    public List<String> scan(String pattern) {
    
    
        ScanOptions options = ScanOptions.scanOptions().match(pattern).build();
        RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
        RedisConnection rc = Objects.requireNonNull(factory).getConnection();
        Cursor<byte[]> cursor = rc.scan(options);
        List<String> result = new ArrayList<>();
        while (cursor.hasNext()) {
    
    
            result.add(new String(cursor.next()));
        }
        try {
    
    
            RedisConnectionUtils.releaseConnection(rc, factory);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return result;
    }
 /**
     * 分页查询 key
     * @param patternKey key
     * @param page 页码
     * @param size 每页数目
     * @return /
     */
    public List<String> findKeysForPage(String patternKey, int page, int size) {
    
    
        ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
        RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
        RedisConnection rc = Objects.requireNonNull(factory).getConnection();
        Cursor<byte[]> cursor = rc.scan(options);
        List<String> result = new ArrayList<>(size);
        int tmpIndex = 0;
        int fromIndex = page * size;
        int toIndex = page * size + size;
        while (cursor.hasNext()) {
    
    
            if (tmpIndex >= fromIndex && tmpIndex < toIndex) {
    
    
                result.add(new String(cursor.next()));
                tmpIndex++;
                continue;
            }
            // 获取到满足条件的数据后,就可以退出了
            if (tmpIndex >= toIndex) {
    
    
                break;
            }
            tmpIndex++;
            cursor.next();
        }
        try {
    
    
            RedisConnectionUtils.releaseConnection(rc, factory);
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return result;
    }
       /**
     * 判断key是否存在
     * @param key 键
     * @return true 存在 false不存在
     */
    public boolean hasKey(String key) {
    
    
        try {
    
    
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     * @param key 可以传一个值 或多个
     */
    public void del(String... key) {
    
    
        if (key != null && key.length > 0) {
    
    
            if (key.length == 1) {
    
    
                redisTemplate.delete(key[0]);
            } else {
    
    
                redisTemplate.delete(CollectionUtils.arrayToList(key));
            }
        }
    }
 /**
     * 普通缓存获取
     * @param key 键
     * @return 值
     */
    public Object get(String key) {
    
    
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 批量获取
     * @param keys
     * @return
     */
    public List<Object> multiGet(List<String> keys) {
    
    
        Object obj = redisTemplate.opsForValue().multiGet(Collections.singleton(keys));
        return null;
    }

    /**
     * 普通缓存放入
     * @param key   键
     * @param value 值
     * @return true成功 false失败
     */
    public boolean set(String key, Object value) {
    
    
        try {
    
    
            redisTemplate.opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @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;
        }
    }

    /**
     * 普通缓存放入并设置时间
     * @param key   键
     * @param value 值
     * @param time  时间
     * @param timeUnit 类型
     * @return true成功 false 失败
     */
    public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
    
    
        try {
    
    
            if (time > 0) {
    
    
                redisTemplate.opsForValue().set(key, value, time, timeUnit);
            } else {
    
    
                set(key, value);
            }
            return true;
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return false;
        }
    }
    //省略其他操作map的方法
}

用户实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OnlineUser {
    
    

    private String userName;

    private String nickName;

    private String job;

    private String browser;

    private String ip;

    private String address;

    private String key;

    private Date loginTime;


}

token配置

securityproerties:

@Data
@Configuration(proxyBeanMethods = false)
@ConfigurationProperties(prefix = "jwt")
public class SecurityProperties {
    
    

    /** Request Headers : Authorization */
    private String header;

    /** 令牌前缀,最后留个空格 Bearer */
    private String tokenStartWith;

    /** 必须使用最少88位的Base64对该令牌进行编码 */
    private String base64Secret;
    private String secret;
    /** 令牌过期时间 此处单位/毫秒 */
    private Long tokenValidityInSeconds;

    /** 在线用户 key,根据 key 查询 redis 中在线用户的数据 */
    private String onlineKey;

    /** 验证码 key */
    private String codeKey;

    public String getTokenStartWith() {
    
    
        return tokenStartWith + " ";
    }
}

其中Yml配置:


#jwt
jwt:
  header: Authorization
  # 令牌前缀
  token-start-with: Bearer
  secret: k09BQnaF
  # 必须使用最少88位的Base64对该令牌进行编码
  base64-secret: ZmQ0ZGI5NjQ0MDQwY2I4MjMxY2Y3ZmI3MjdhN2ZmMjNhODViOTg1ZGE0NTBjMGM4NDA5NzYxMjdjOWMwYWRmZTBlZjlhNGY3ZTg4Y2U3YTE1ODVkZDU5Y2Y3OGYwZWE1NzUzNWQ2YjFjZDc0NGMxZWU2MmQ3MjY1NzJmNTE0MzI=
  # 令牌过期时间 此处单位/毫秒 ,默认4小时,可在此网站生成 https://www.convertworld.com/zh-hans/time/milliseconds.html
  token-validity-in-seconds: 14400000
  # 在线用户key
  online-key: online-token
  # 验证码
  code-key: code-key

service层保存和查询在线用户

在线用户层

@Service
@Slf4j
public class OnlineUserService {
    
    

    private final SecurityProperties properties;//这个特性类的属性在上面
    private RedisUtils redisUtils;
    public OnlineUserService(SecurityProperties properties, RedisUtils redisUtils) {
    
    
        this.properties = properties;
        this.redisUtils = redisUtils;
    }
 /**
     * 保存在线用户信息
     * @param jwtUser /
     * @param token /
     * @param request /
     */
    public void save(JwtUser jwtUser, String token, HttpServletRequest request) {
    
    
        String job = jwtUser.getDept() + "/" + jwtUser.getJob();
        String ip = StringUtils.getIp(request);
        String browser = StringUtils.getBrowser(request);
        String address = StringUtils.getCityInfo(ip);
        OnlineUser onlineUser = null;
        try {
    
    
            onlineUser = new OnlineUser(jwtUser.getUsername(), jwtUser.getNickName(), job, browser, ip, address, EncryptUtils.desEncrypt(token), new Date());
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        redisUtils.set(properties.getOnlineKey() + token, onlineUser, properties.getTokenValidityInSeconds() / 1000);
    }
  /**
     * 查询全部数据 分页
     * @param filter /
     * @param pageable /
     * @return /
     */
    public Map<String, Object> getAll(String filter, int type, Pageable pageable) {
    
    
        List<OnlineUser> onlineUsers = getAll(filter, type);
        return PageUtil.toPage(
                PageUtil.toPage(pageable.getPageNumber(), pageable.getPageSize(), onlineUsers),
                onlineUsers.size()
        );
    }

   /**
     * 查询全部数据,不分页
     * @param filter /
     * @return /
     */
    public List<OnlineUser> getAll(String filter, int type) {
    
    
        List<String> keys = null;
        if (type == 1) {
    
    
            keys = redisUtils.scan("m-online-token*");
        } else {
    
    
            keys = redisUtils.scan(properties.getOnlineKey() + "*");
        }
    Collections.reverse(keys);
        List<OnlineUser> onlineUsers = new ArrayList<>();
        for (String key : keys) {
    
    
            OnlineUser onlineUser = (OnlineUser) redisUtils.get(key);
            if (StringUtils.isNotBlank(filter)) {
    
    
                if (onlineUser.toString().contains(filter)) {
    
    
                    onlineUsers.add(onlineUser);
                }
            } else {
    
    
                onlineUsers.add(onlineUser);
            }
        }
        onlineUsers.sort((o1, o2) -> o2.getLoginTime().compareTo(o1.getLoginTime()));
        return onlineUsers;
    }
  /**
     * 踢出用户
     * @param key /
     * @throws Exception /
     */
    public void kickOut(String key) throws Exception {
    
    
        key = properties.getOnlineKey() + EncryptUtils.desDecrypt(key);
        redisUtils.del(key);

    }

    /**
     * 踢出移动端用户
     * @param key /
     * @throws Exception /
     */
    public void kickOutT(String key) throws Exception {
    
    

        String keyt = "m-online-token" + EncryptUtils.desDecrypt(key);
        redisUtils.del(keyt);

    }

    /**
     * 退出登录
     * @param token /
     */
    public void logout(String token) {
    
    
        String key = properties.getOnlineKey() + token;
        redisUtils.del(key);
    }
  /**
     * 检测用户是否在之前已经登录,已经登录踢下线
     * @param userName 用户名
     */
    public void checkLoginOnUser(String userName, String igoreToken) {
    
    
        List<OnlineUser> onlineUsers = getAll(userName, 0);
        if (onlineUsers == null || onlineUsers.isEmpty()) {
    
    
            return;
        }
        for (OnlineUser onlineUser : onlineUsers) {
    
    
            if (onlineUser.getUserName().equals(userName)) {
    
    
                try {
    
    
                    String token = EncryptUtils.desDecrypt(onlineUser.getKey());
                    if (StringUtils.isNotBlank(igoreToken) && !igoreToken.equals(token)) {
    
    
                        this.kickOut(onlineUser.getKey());
                    } else if (StringUtils.isBlank(igoreToken)) {
    
    
                        this.kickOut(onlineUser.getKey());
                    }
                } catch (Exception e) {
    
    
                    log.error("checkUser is error", e);
                }
            }
        }
    }
    }

工具类 获得用户浏览器等其他信息

上面的StringUtils工具类有一些获取信息的方法:

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.UserAgent;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.Date;
public class StringUtils extends org.apache.commons.lang3.StringUtils {
    
    

    /**
     * 获取ip地址
     */
    public static String getIp(HttpServletRequest request) {
    
    
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
    
    
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
    
    
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
    
    
            ip = request.getRemoteAddr();
        }
        String comma = ",";
        String localhost = "127.0.0.1";
        if (ip.contains(comma)) {
    
    
            ip = ip.split(",")[0];
        }
        if (localhost.equals(ip)) {
    
    
            // 获取本机真正的ip地址
            try {
    
    
                ip = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
    
    
                e.printStackTrace();
            }
        }
        return ip;
    }

    /**
     * 根据ip获取详细地址
     */
    public static String getCityInfo(String ip) {
    
    
        String api = String.format(YshopConstant.Url.IP_URL, ip);
        JSONObject object = JSONUtil.parseObj(HttpUtil.get(api));
        return object.get("addr", String.class);
    }
    //获取用户浏览器名字
    public static String getBrowser(HttpServletRequest request) {
    
    
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        Browser browser = userAgent.getBrowser();
        return browser.getName();
    }
        /**
     * 获得当天是周几
     */
    public static String getWeekDay() {
    
    
        String[] weekDays = {
    
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());

        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
    
    
            w = 0;
        }
        return weekDays[w];
    }
}

controller层

@RestController
@RequestMapping("/auth/online")
@Api(tags = "系统:在线用户管理")
public class OnlineController {
    
    

    private final OnlineUserService onlineUserService;

    public OnlineController(OnlineUserService onlineUserService) {
    
    
        this.onlineUserService = onlineUserService;
    }

    @ApiOperation("查询在线用户")
    @GetMapping
    @PreAuthorize("@el.check()")
    public ResponseEntity<Object> getAll(@RequestParam(value = "filter", defaultValue = "") String filter,
                                         @RequestParam(value = "type", defaultValue = "0") int type,
                                         Pageable pageable) {
    
    
        return new ResponseEntity<>(onlineUserService.getAll(filter, type, pageable), HttpStatus.OK);
    }

    @ApiOperation("踢出用户")
    @DeleteMapping
    @PreAuthorize("@el.check()")
    public ResponseEntity<Object> delete(@RequestBody Set<String> keys) throws Exception {
    
    

        for (String key : keys) {
    
    
            onlineUserService.kickOut(key);
        }
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @ForbidSubmit
    @ApiOperation("踢出移动端用户")
    @PostMapping("/delete")
    @PreAuthorize("@el.check()")
    public ResponseEntity<Object> deletet(@RequestBody Set<String> keys) throws Exception {
    
    

        for (String key : keys) {
    
    
            onlineUserService.kickOutT(key);
        }
        return new ResponseEntity<>(HttpStatus.OK);
    }

猜你喜欢

转载自blog.csdn.net/qq_41358574/article/details/121832755