项目名称:IM系统
项目功能:实现一个类似微信聊天功能web网站
技术栈:
M — model — 数据管理模块
基于MySQL数据库进行数据管理
V — controller — 服务控制管理模块(业务逻辑模块)
搭建http服务器针对不同的请求提供不同的服务(博客页面的获取以及博客数据的增删查改)
C — 前端界面模块 基于html+css+js实现前端界面的展示
注意事项 :
- MySQL数据库对语句中大小写不敏感
- 库名, 表名, 表中字段不能使用关键字
- 每条语句最后都要以英文分号结尾
sql文件
package com.xky.imchat.util;
//JWT工具类
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.xky.imchat.entity.vo.Admin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.HashMap;
import java.util.Stack;
public class JwtUtil {
//设置过期时间
private static final long EXPIRE_TIME = 15*60*1000;
private static Logger logger = LoggerFactory.getLogger(JwtUtil.class);
//token私钥,尽量还是不统一为好
private static final String TOKEN_SECRET = "8ae0d24822ef59d9e75745449b3501bc";
// 生成签名,有效时间为15分钟
public static String sign(String username,String userId){
//设置过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//私钥加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头信息
HashMap header= new HashMap<>(2);
header.put("type","jwt");
header.put("alg","HS256");
return JWT.create()
.withAudience(userId)
.withClaim("username",username)
.withClaim("userId",userId)
.withExpiresAt(date)
.sign(algorithm);
}
//校验token是否正确
public static boolean verity(String token){
try{
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier build = JWT.require(algorithm).build();
DecodedJWT verify = build.verify(token);
return true;
}catch (Exception e){
return false;
}
}
//获取签发对象
public static String getAudience(String token){
String audience = null;
try{
audience = JWT.decode(token).getAudience().get(0);
}catch (Exception e){
}
return audience;
}
//获取载荷的内容
public static Claim getClaimByName(String token,String name){
return JWT.decode(token).getClaim(name);
}
//提取相应的内容
public static Admin getContent(String token){
try{
Algorithm algorithm=Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier verifier=JWT.require(algorithm).build();
DecodedJWT jwt=verifier.verify(token);
Admin a = new Admin();
a.setUserID(jwt.getClaim("userId").asString());
a.setUserName(jwt.getClaim("username").asString());
System.out.println(a);
return a;
}catch (Exception e){
e.printStackTrace();
logger.info("token出错了");
}
return null;
}
//刷新token对象
public static String refresh(String username,String userId){
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME*2);
//私钥加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头信息
HashMap header= new HashMap<>(2);
header.put("type","jwt");
header.put("alg","HS256");
return JWT.create()
.withAudience(userId)
.withClaim("username",username)
.withClaim("userId",userId)
.withExpiresAt(date)
.sign(algorithm);
}
public static String refresh(String token){
Admin content = JwtUtil.getContent(token);
String refresh = JwtUtil.sign(content.getUserName(), content.getUserID());
return refresh;
}
}
后端拦截器实现(带令牌刷新功能及异端登录挤掉功能)
import com.xky.imchat.annotation.PassToken;
import com.xky.imchat.entity.vo.Admin;
import com.xky.imchat.util.JwtUtil;
import com.xky.imchat.util.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//后端拦截器实现
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
public class JwtInterceptor implements HandlerInterceptor {
Logger logger = LoggerFactory.getLogger(JwtInterceptor.class);
private RedisUtil redisUtil;
public JwtInterceptor(RedisUtil redisUtil){
this.redisUtil = redisUtil;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从请求头里面获取token
String token = request.getHeader("Authorization");
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod)handler;
Method method = handlerMethod.getMethod();
//检查是否有passtoken注解
if(method.isAnnotationPresent(PassToken.class)){
PassToken annotation = method.getAnnotation(PassToken.class);
if(annotation.required()){
return true;
}
}else{
logger.info("要进行token检验");
if(token==null){
logger.info("token无效,需要重新登录");
return false;
}
if(!JwtUtil.verity(token)){
if(redisUtil.hasKey(token)){
response.setStatus(201);
String new_token =(String) redisUtil.get(token);
//移除原来的缓存
redisUtil.delete(token);
logger.info(new_token);
String refresh = JwtUtil.refresh(new_token);
response.setHeader("new_token",refresh);
//更新缓存
logger.info("开始更新缓存");
Admin content = JwtUtil.getContent(new_token);
redisUtil.set(content.getUserName(),refresh,900);
String access_token = JwtUtil.refresh(content.getUserName(), content.getUserID());
System.out.println(redisUtil.hasKey(content.getUserName()));
redisUtil.set(refresh,access_token,1800);
return true;
}else{
response.setStatus(202);
return false;
}
}else{
//token还在有效期,但是其他客户端登录了要退出登录
Admin content = JwtUtil.getContent(token);
String new_token = (String)redisUtil.get(content.getUserName());
System.out.println("开始排挤对方");
logger.info(token);
logger.info(new_token);
System.out.println(new_token.equals(token));
if(!new_token.equals(token)){
logger.info("开始挤掉对方");
response.setStatus(203);
return false;
}
}
}
return JwtUtil.verity(token);
}
@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 {
}
}
前端拦截器实现
axios.interceptors.request.use(
config => {
//请求成功时,对请求作拦截处理
if (localStorage.getItem('Authorization')) {
config.headers.Authorization = localStorage.getItem('Authorization');
console.log("拦截请求,添加token")
}else{
console.log('拦截请求,没有token')
}
return config;
},
error => {//请求失败时
console.log(error);
return Promise.reject();
}
);
axios.interceptors.response.use(
response => {
if (response.data.code ===200 ) {
console.log(response)
return response.data;
}else{
console.log(response)
if(response.status==202){
localStorage.removeItem('Authorization');
//直接跳转到登录页面
router.push({
path: "/login"
});
}else if(response.status==201){
localStorage.setItem('Authorization',response.headers.new_token)
}else if(response.status==203){
router.push({
path: "/login",
params:{
id:203
}
});
}
return response
}
},
error => {
// console.log(error);
return Promise.reject();
}
);
axios.interceptors.request.use(
config => {
//请求成功时,对请求作拦截处理
if (localStorage.getItem('Authorization')) {
config.headers.Authorization = localStorage.getItem('Authorization');
console.log("拦截请求,添加token")
}else{
console.log('拦截请求,没有token')
}
return config;
},
error => {//请求失败时
console.log(error);
return Promise.reject();
}
);
axios.interceptors.response.use(
response => {
if (response.data.code ===200 ) {
console.log(response)
return response.data;
}else{
console.log(response)
if(response.status==202){
localStorage.removeItem('Authorization');
//直接跳转到登录页面
router.push({
path: "/login"
});
}else if(response.status==201){
localStorage.setItem('Authorization',response.headers.new_token)
}else if(response.status==203){
router.push({
path: "/login",
params:{
id:203
}
});
}
return response
}
},
error => {
// console.log(error);
return Promise.reject();
}
);
@ChannelHandler.Sharable
@Component
@Slf4j
public class ImHandler extends SimpleChannelInboundHandler
前端代码
后端代码