package com.songzhen.howcool.auth;
import com.alibaba.fastjson.JSON;
import com.songzhen.howcool.model.enums.RetCodeEnum;
import com.songzhen.howcool.util.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
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;
import java.util.HashMap;
import java.util.Map;
public class AuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 預處理回調方法,實現處理器的預處理(如檢查登錄),第三個參數爲響應的處理器,自定義Controller
* 返回值:true表示繼續流程(如調用下一個攔截器或處理器);false表示流程中斷(如登陸檢查失敗),不會繼續調用其餘的攔截器或處理器,此時咱們須要經過response來產生響應;
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
// 判斷對象是不是映射到一個方法,若是不是則直接經過
if (!(object instanceof HandlerMethod)) {
// instanceof運算符是用來在運行時指出對象是不是特定類的一個實例
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//檢查方法是否有NeedLogin註解,無則跳過認證
if (method.isAnnotationPresent(NeedLogin.class)) {
NeedLogin needLogin = method.getAnnotation(NeedLogin.class);
if (needLogin.required()) {
// 從HTTP請求頭中獲取TOKEN信息
String token = httpServletRequest.getHeader("Authorization");
// HTTP請求頭中TOKEN解析出的用戶信息
String uid = JwtUtil.getUid(token);
String userName = JwtUtil.getUserName(token);
String realName = JwtUtil.getRealName(token);
// 檢查TOKEN
if (!checkToken(token)) {
// TOKEN錯誤時,提示用戶登陸
Map<String, Object> retMap = new HashMap<>(16);
retMap.put("code", RetCodeEnum.ACCOUNT_UNAUTHORIZED.getCode());
retMap.put("msg", RetCodeEnum.ACCOUNT_UNAUTHORIZED.getDesc());
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.getWriter().append(JSON.toJSONString(retMap));
return false;
}
// 組裝用戶信息到REQUEST中
Map<String, Object> currentUser = new HashMap<>(16);
currentUser.put("uid", uid);
currentUser.put("userName", userName);
currentUser.put("realName", realName);
httpServletRequest.setAttribute("currentUser", currentUser);
return true;
}
}
return true;
}
/**
* 後處理回調方法,實現處理器的後處理(但在渲染視圖以前),此時咱們能夠經過modelAndView(模型和視圖對象)對模型數據進行處理或對視圖進行處理,modelAndView也可能爲null。
*/
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object, ModelAndView modelAndView) throws Exception {
long now = System.currentTimeMillis();
}
/**
* 整個請求處理完畢回調方法,即在視圖渲染完畢時回調,如性能監控中咱們能夠在此記錄結束時間並輸出消耗時間,還能夠進行一些資源清理,相似於try-catch-finally中的finally,但僅調用處理器執行鏈中
*/
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
/**
* 檢查TOKEN
*
* @param token token
* 要校驗的token
* @return boolean
* true:經過 false:失敗
*/
private boolean checkToken(String token) {
// ------------------------認證------------開始-----------------
if (null == token) {
return false;
}
// 獲取TOKEN中的用戶信息
String uid = JwtUtil.getUid(token);
// 根據uid從redis中獲取用戶tokenInRedis
String tokenInRedis = redisTemplate.opsForValue().get(uid);
if (null == tokenInRedis) {
// 若是REDIS異常,返回成功保證正常業務能夠繼續處理
return true;
}
// HTTP請求頭中TOKEN解析出的用戶信息
String userName = JwtUtil.getUserName(token);
String realName = JwtUtil.getRealName(token);
String deviceId = JwtUtil.getDeviceId(token);
long expireIn = JwtUtil.getExpireIn(token);
// REDIS服務器中TOKEN解析出的用戶信息
String userNameInRedis = JwtUtil.getUserName(tokenInRedis);
String realNameInRedis = JwtUtil.getRealName(tokenInRedis);
String deviceIdInRedis = JwtUtil.getDeviceId(tokenInRedis);
long expireInInRedis = JwtUtil.getExpireIn(tokenInRedis);
if (null == userName || null == realName || null == deviceId) {
return false;
}
if (null == userNameInRedis || null == realNameInRedis || null == deviceIdInRedis) {
return false;
}
// 判斷TOKEN是否過時
if (expireIn != expireInInRedis) {
return false;
}
if (expireIn < System.currentTimeMillis()) {
return false;
}
// ------------------------認證------------結束-----------------
return true;
}
}