本帖最后由 ppgjx 于 2022-5-21 23:09 编辑
我现在在做一个用户系统,基于token认证的,现在想建立一个websocket服务所有登录的用户都链接这个websocket服务器 ,但是websocket如何实时踢人下线这个问题困扰了我很久
这是我的登录校验代码,可以到看到每次请求都去检测用户带来的uid和token是否和redis里面的匹配
[Asm] 纯文本查看 复制代码 package com.ppgjx.app.interceptor;
import com.ppgjx.app.annotation.PxCheckLogin;
import com.ppgjx.app.annotation.PxCheckRole;
import com.ppgjx.app.common.user.ans.common.UserInfo;
import com.ppgjx.app.common.user.ans.common.UserRoleInfo;
import com.ppgjx.app.common.user.ans.common.UserRolePermissionInfo;
import com.ppgjx.app.constant.RedisKey;
import com.ppgjx.app.constant.ResultCode;
import com.ppgjx.app.execption.ResRunException;
import com.ppgjx.app.util.MyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
* 用户登录拦截器
*
*/
@Slf4j
public class UserLoginInterceptor extends HandlerInterceptorAdapter {
@Autowired
private StringRedisTemplate stringRedisTemplate;
//过滤未登录用户
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
this.hasPermission(handler,request);
return true;
}
//判断是否登录
private void isLogin(HttpServletRequest request){
String uid = request.getHeader("uid");
String token = request.getHeader("token");
if(null != uid && null != token){
String redisUid = stringRedisTemplate.opsForValue().get(String.format(RedisKey.LOGIN_USER_TOKEN,uid ));
if(!token.equals(redisUid)){
throw new ResRunException(ResultCode.OTHER_LOGIN_ERR);
}
}else {
throw new ResRunException(ResultCode.NOT_LOGIN_ERR);
}
}
/**
* 是否有权限
*/
private boolean hasPermission(Object handler,HttpServletRequest request) {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
//获取类上的注解
PxCheckLogin pxCheckLoginClass = handlerMethod.getMethod().getDeclaringClass().getAnnotation(PxCheckLogin.class);
// 获取方法上的注解
PxCheckLogin pxCheckLogin = handlerMethod.getMethod().getAnnotation(PxCheckLogin.class);
//如果类上加了注解进行拦截
if(null != pxCheckLoginClass){
//如果这个方法有注解 且 设置了为不登录则通过 否则检验
if(null != pxCheckLogin && !pxCheckLogin.value()){
return true;
}else {
this.isLogin(request);
}
}else {
//如果类上没注册则检查方法
if(null != pxCheckLogin && pxCheckLogin.value()){
this.isLogin(request);
}
}
}
return true;
}
}
这是我的websockert服务器代码 只是做了简单的逻辑 没有做登录 我现在想的是 怎么才能让用户校验类 如果判断用户下线了 会给websocket一个回调 这样就完美的解决及时下线的问题 但是我实在不知道这个回调改怎么写 希望有大佬帮帮我
[Asm] 纯文本查看 复制代码 package com.ppgjx.app.netty.user;
import com.ppgjx.app.service.PxUserService;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Slf4j
public class UserChannelHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
private static StringRedisTemplate redisTemplate;
private static PxUserService pxUserService;
@Resource
public void setStringRedisTemplate(StringRedisTemplate redisTemplate) {
UserChannelHandler.redisTemplate = redisTemplate;
}
@Resource
public void setPxUserService(PxUserService pxUserService) {
UserChannelHandler.pxUserService = pxUserService;
}
//用户id=>channel示例
//可以通过用户的唯一标识保存用户的channel
//这样就可以发送给指定的用户
public static ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//客户端断开链接
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("客户端断开连接 --- " + ctx.channel().id() );
clients.remove(ctx.channel());
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("channelRead");
super.channelRead(ctx, msg);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
//获取客户端消息
String context = msg.text();
log.info(ctx.channel().id().asShortText() + " 测试数据 ---" + context + " -- 当前客户数量 --- " + clients.size());
pushAllUser( "用户: " + ctx.channel().id().asShortText() + " 消息: " + context + " 当前客户数量: " + clients.size());
}
/**
* 推送所有
* [url=home.php?mod=space&uid=952169]@Param[/url] text
*/
public void pushAllUser(String text){
clients.writeAndFlush(new TextWebSocketFrame(text));
}
/**
* 推送所有除了自己
* @param text
* @param ctx
*/
public void pushOtherUser(String text,ChannelHandlerContext ctx){
clients.forEach(ch ->{
if(ctx.channel() != ch){
ch.writeAndFlush(new TextWebSocketFrame(text));
}
});
}
//客户端建立连接
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
System.out.println("客户端建立连接 --- " + ctx.channel().id() );
//添加channels
clients.add(ctx.channel());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("异常 --- " + ctx.channel().id());
ctx.channel().close();
clients.remove(ctx.channel());
}
}
|