怎么实现同一账号只能在一台设备登录

您所在的位置:网站首页 登陆时如何只显示一个账户 怎么实现同一账号只能在一台设备登录

怎么实现同一账号只能在一台设备登录

2023-12-21 02:32| 来源: 网络整理| 查看: 265

同一账号只能在一台设备登录实现思路。

注意:登录是在白名单(直接放行的接口)。生成的token携带账号信息。

1.用户每次登录生成token时,将账号当成key,token当成value,以token的过期时间存入redis中。

2.用户访问的时候,在拦截器解析token,获取账号,拿账号去redis中获取value,如果是value的token与当前用户携带过来的token一致就放行。如果不一致,则告诉前端重复登录,让前端清除token,跳转到登录页面。

3.用户在另一台设备登录时,也是存入redis,这样就刷新了token的值和redis的过期时间。

这是我的一个实现思路,有没有大佬可以指点,互相学习一下。 在这里插入图片描述 写了个小demo,主要代码如下:

1.拦截器代码:

package com.yblue.config; import com.yblue.dto.Payload; import com.yblue.dto.UserInfo; import net.minidev.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.http.HttpMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; /** * @author: JiaXinMa * @description: 解决跨域和权限 * @date: 2021/3/26 */ public class AuthInterceptor implements HandlerInterceptor { @Autowired FilterProperties filterProperties; @Autowired JwtProperties jwtProps; @Autowired StringRedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //表示接受任意域名的请求,也可以指定域名 response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin")); //该字段可选,是个布尔值,表示是否可以携带cookie response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS"); response.setHeader("Access-Control-Allow-Headers", "*"); //预请求,这里可以不加,但是其他语言开发的话记得处理options请求 // 如:异步请求的时候前端发两次请求,其中一次是测试通不通 if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) { return true; } //获取当前访问的URL接口后的路径 String uri = request.getRequestURI(); //如:/api/user/xxx //判断uri地址,如果为 /error 则可能是请求方式、参数、访问路径不对 if (uri.equals("/error")) { this.responseError(response, 405, "请求方式、参数类型、访问地址错误、数据库方面异常!"); return false; } //1.判断当前访问路径是否在白名单列表中 List allowPaths = filterProperties.getAllowPaths(); for (String allowPath : allowPaths) { if (uri.contains(allowPath)) { //如果在,直接放行了 return true; } } //2.校验token合法性 Payload payload = null; try { String token = request.getHeader(jwtProps.getCookie().getCookieName()); // String token = CookieUtils.getCookieValue(request, jwtProps.getCookie().getCookieName()); payload = JwtUtils.getInfoFromToken(token, jwtProps.getPublicKey(), UserInfo.class); String redisToken = redisTemplate.opsForValue().get(payload.getInfo().getUsername()); if (token.equals(redisToken)) { return true; } else { this.responseError(response, 401, "您的账号在另一台设备上登录,请及时修改密码!"); } } catch (Exception e) { this.responseError(response, 401, "登录失效或未登录!"); return false; } //3.如果你的想校验权限、角色,可以在你的userInfo里封装,然后在下面验证,不过我现在的userInfo没放 // UserInfo userInfo = payload.getInfo(); // List modules = userInfo.getModules(); // for (Module module : modules) { // if (uri.equals(module.getUrl())) {//判断用户是否有访问路径的权限 // return true; // } else { // this.responseError(response, 403, "权限不足!"); // } // } return false; } /** * @author: JiaXinMa * @description: 响应错误代码 处理响应错误信息的方法,可以拿去用 * @date: 2021/8/23 */ public void responseError(HttpServletResponse response, Integer code, String returnMessage) { response.setCharacterEncoding("UTF-8"); response.setContentType("application/json; charset=utf-8"); JSONObject jsonObject = new JSONObject(); try { jsonObject.put("code", code); jsonObject.put("msg", returnMessage); response.getWriter().append(jsonObject.toString()); } catch (Exception e) { e.printStackTrace(); } } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }

2.业务层代码:

package com.yblue.service; import com.yblue.config.JwtProperties; import com.yblue.config.JwtUtils; import com.yblue.domain.User; import com.yblue.dto.Payload; import com.yblue.dto.UserInfo; import com.yblue.exception.pojo.ExceptionEnum; import com.yblue.exception.pojo.MdException; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; /** * @author: JiaXinMa * @description: 验证授权 Service层 * @date: 2021/3/25 */ @Service @Transactional public class AuthService { @Autowired UserService userService; @Autowired private JwtProperties jwtProps; @Autowired private StringRedisTemplate redisTemplate; /** * @author: JiaXinMa * @description: 登录 * @date: 2021/5/28 */ public Map login(String username, String password) { try { Map map = new HashMap();//存放token和用户信息 //1. 判断用户名和密码是否正确 User loginUser = userService.findUserByNameAndPwd(username, password); UserInfo userInfo = new UserInfo(loginUser.getUserId(), loginUser.getName(), loginUser.getUsername()); //2.生成token String token = this.generateToken(userInfo); map.put(jwtProps.getCookie().getCookieName(), token); map.put("userInfo", userInfo); //3.将token放进redis中 redisTemplate.opsForValue().set(loginUser.getUsername(), token, 60, TimeUnit.MINUTES); return map; } catch (Exception e) { throw new MdException(ExceptionEnum.INVALID_USERNAME_PASSWORD); } } /** * @author: JiaXinMa * @description: 生成token * @date: 2021/4/6 */ public String generateToken(UserInfo info) { //利用JwtUtils+私钥生成加密token //以前是的工具类是通过cookies带过去了,然后现在他们说要放在请求头上,所以那些类名字没改,不妨碍业务的使用 return JwtUtils.generateTokenExpireInMinutes(info, jwtProps.getPrivateKey(), jwtProps.getCookie().getExpire()); } }

3.控制层代码

package com.yblue.controller; import com.yblue.domain.User; import com.yblue.service.AuthService; import com.yblue.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.Map; /** * @author: JiaXinMa * @description: 验证 控制器 * @date: 2021/5/28 */ @Slf4j @RestController @RequestMapping("/api") public class AuthController { @Autowired AuthService authService; @Autowired UserService userService; @PostMapping("/auth/login") public Map login(@RequestBody User user) { log.info("/api/auth/login:{}", user); return authService.login(user.getUsername(), user.getPassword()); } @GetMapping("/user/findByUserId") public User findByUserId(@RequestParam("userId") Integer userId) { log.info("/api/user/findByUserId:{}", userId); return userService.findByUserId(userId); } }

效果如下图:

第一次: 在这里插入图片描述 第二次: 在这里插入图片描述 如果有些小伙伴有更好的解决方案或者觉得该方案有什么不足的,可以提出来一起讨论交流。

想看更多精彩内容,可以关注我的博客园 我的博客园



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3