1.前言
需要实现类似QQ等账号异地登陆设备的检测有诸多方法,本例使用比较常用的一种方法就是获取Mac地址后保存到Redis当中,当在不同的设备登录时会检测当机的Mac地址与保存在Redis中的Mac地址是否匹配,若不匹配则拒绝登录。
那么,正片开始!!!
2.Spring Boot项目创建
这是整个项目的大致目录,各位可以参考一下,但不需要完全相同
等待IDEA解析完成后即可开始编码
3.编码
首先在application.properties中进行配置
spring.datasource.url=jdbc:mysql://localhost:3306/你的数据库名称?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=PRC&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=123456
spring.banner.location=classpath:1.txt
spring.servlet.multipart.enabled=true
#mybatis相关配置
mybatis.mapperLocations=classpath:mapper/*.xml
mybatis.type-aliases-package=springboot_olmusic.pojo
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#Redis相关配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=
spring.redis.timeout=30000
spring.redis.database=0
spring.redis.pool.max-wait=-1
在pojo下创建名为User的实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer uid;
private String userCode;
private String password;
}
在src/mapper下创建名为UserMapper的接口文件
import org.apache.ibatis.annotations.Mapper;
import springboot_olmusic.pojo.User;
@Mapper
public interface UserMapper {
User selectUserCodeBypwd(String userCode);
}
在resource/mapper中创建xml文件
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE mapper PUBLIC
"-//mybatis.org//DTD com.example.Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="你的接口文件地址">
<select id="selectUserCodeBypwd" resultType="springboot_olmusic.pojo.User">
select * from oluser
where userCode = #{userCode}
</select>
</mapper>
服务层代码:
import cn.hutool.json.JSONUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import springboot_olmusic.mapper.UserMapper;
import springboot_olmusic.pojo.User;
import springboot_olmusic.utils.*;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.Objects;
@Service
public class UserService {
MacAddressUtil macAddressUtil = new MacAddressUtil();
@Resource
private UserMapper userMapper;
@Resource
private RedisTemplate redisTemplate;
public static long USER_TIME = 5 * 60 * 1000;
private static final String USER_KEY = "user:";
private static final String MACAddress = "macAddress:";
public ResponseResult login(String userCode, String password){
Object userObj = null;
if (Objects.isNull(userCode) || Objects.isNull(password)
|| userCode == "" || password == ""){
return new ResponseResult(StatusCode.USER_PASSWORD_NULL, "账号或密码不能为空");
}
userObj = redisTemplate.opsForValue().get(USER_KEY + userCode);
if (userObj != null) {
String obj = String.valueOf(redisTemplate.opsForValue().get(MACAddress + userCode));
System.out.println("aaaaaaaaa:" + obj);
String macAddress = macAddressUtil.getMacAddress();
if (!obj.equals(macAddress)){
return new ResponseResult(StatusCode.DATA_EXIST, "账号已在别处登录!");
}
}
User user = userMapper.selectUserCodeBypwd(userCode);
if (Objects.isNull(user)){
return new ResponseResult(StatusCode.USER_PASSWORD_NULL, "该用户不存在!");
}
if(!user.getPassword().equals(password)){
return new ResponseResult(StatusCode.PARAM_ERROR, "密码错误");
}
String token = CreateJwt.getoken(String.valueOf(user.getUid()), user.getUserCode());
String macAddress = macAddressUtil.getMacAddress();
System.out.println("本机Mac地址为" + macAddress);
redisTemplate.opsForValue().set(MACAddress + userCode, macAddress);
redisTemplate.opsForValue().set(USER_KEY + userCode,
JSONUtil.parse("{\"token\":" + token + ", \"user\":"
+ JSONUtil.parse(user) + "}"));
Duration duration= Duration.ofMillis(USER_TIME);
redisTemplate.expire(USER_KEY + userCode, duration);
return new ResponseResult(StatusCode.SERVER_RUN_SUCCESS,
JSONUtil.parse("{\"token\":" + token + ", \"user\":"
+ JSONUtil.parse(user) + "}"),
"用户" + user.getNickname() + "登录成功!")
;
}
}
其中需要的工具类也给各位奉上
获取本机Mac地址:
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
public class MacAddressUtil {
public String getMacAddress() {
try {
InetAddress localHost = InetAddress.getLocalHost();
NetworkInterface networkInterface = NetworkInterface.getByInetAddress(localHost);
byte[] macAddressBytes = networkInterface.getHardwareAddress();
StringBuilder macAddressBuilder = new StringBuilder();
for (int i = 0; i < macAddressBytes.length; i++) {
macAddressBuilder.append(String.format("%02X%s", macAddressBytes[i], (i < macAddressBytes.length - 1) ? "-" : ""));
}
return macAddressBuilder.toString();
} catch (UnknownHostException | SocketException e) {
e.printStackTrace();
}
return null;
}
}
JWT令牌
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class CreateJwt {
//有效期为
public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000 一个小时
public static String getoken(String uid, String userCode) {
//Jwts.builder()生成
//Jwts.parser()验证
JwtBuilder jwtBuilder = Jwts.builder()
.setId(uid + "")
.setSubject(userCode) //用户名
.setIssuedAt(new Date())//登录时间
.signWith(SignatureAlgorithm.HS256, "Like You Do").setExpiration(new Date(new
Date().getTime() + 30000));
// .signWith(SignatureAlgorithm.HS256, "Like You Do").setExpiration(new Date(JWT_TTL));
//设置过期时间
return jwtBuilder.compact();
}
}
集体响应类
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ResponseResult {
/**
* 状态码
*/
private Integer statusCode;
/**
* 数据
*/
private Object data;
/**
* 提示信息
*/
private String message;
public ResponseResult(Integer statusCode, String message) {
this(statusCode, null, message);
}
}
状态码类
public class StatusCode {
/**
* 成功
*/
public static final int SERVER_RUN_SUCCESS = 200;
/**
* 数据不存在
*/
public static final int DATA_NOT_EXISTS = 400;
/**
* 密码错误
*/
public static final int PASSWORD_ERROR = 401;
/**
* 添加失败
*/
public static final int ADD_FAIL = 402;
/**
* 删除失败
*/
public static final int DELETE_FAIL = 403;
/**
* 更新失败
*/
public static final int UPDATE_FAIL = 408;
/**
* 用户不存在
*/
public static final int USER_NOT_EXIST = 405;
/**
* 数据已存在
*/
public static final int DATA_EXIST = 406;
/**
* 参数错误
*/
public static final Integer PARAM_ERROR = 407;
/**
* 用户名或密码为空
*/
public static final Integer USER_PASSWORD_NULL = 404;
/**
* 用户名或密码为空
*/
public static final Integer REQUEST = 505;
}
controller层相应代码
import cn.hutool.db.handler.StringHandler;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springboot_olmusic.service.UserService;
import springboot_olmusic.utils.ResponseResult;
import javax.annotation.Resource;
@RestController
@RequestMapping("/user")
public class UserController {
@Resource
private UserService userService;
@PostMapping("/login")
public ResponseResult login(@RequestParam String userCode, String password){
return userService.login(userCode, password);
}
}
4.测试
最后,本文仅供参考,若发现bug欢迎指正